首页资源分类应用软件matlab > matlab教程

matlab教程

已有 451617个资源

下载专区

上传者其他资源

    文档信息举报收藏

    标    签:matlab

    分    享:

    文档简介

    matlab7.0从入门到精通

    文档预览

    MATLAB 7.0 从入门到精通 求是科技 编著 人民邮电出版社 图书在版编目(CI P )数据 MATLAB 7.0 从入门到精通/求是科技编著.—北京:人民邮电出版社,2006.3 ISBN 7-115-14327-7 Ⅰ.M… Ⅱ.求… Ⅲ.计算机辅助计算-软件包,MATLAB 7.0 Ⅳ.TP391.75 中国版本图书馆 CIP 数据核字(2006)第 007936 号 内容提要 本书对 MATLAB 7.0 进行了详细的介绍,力求做到细致全面。全书共分为 15 章。前 5 章是有关 MATLAB 的基础知识,包括 MATLAB 的安装、卸载及系统功能的简述,MATLAB 的数学运算和数据可视化工具以及 MATLAB 的编程等内容。第 6 章~第 9 章是 MATLAB 的高级应用部分,分别介绍了 MATLAB 7.0 的数据分 析和处理功能,符号计算功能,Simulink 仿真环境和文件 I/O 操作。第 10 章~第 12 章和第 14 章介绍了信 号处理工具箱、图像处理工具箱和编译工具箱以及应用广泛的高级图形设计。第 13 章、第 15 章介绍了 MATLAB 的外围功能,包括与 Word、Excel 的混合使用和 MATLAB 的应用程序接口。附录部分列出了常用 命令和函数,Simulink 的库模块和应用程序接口函数库。 本书叙述详细,深入浅出,又有丰富的例程,适合使用 MATLAB 的本科生、研究生和教师以及广大科 研工作人员作为参考用书。 MATLAB 7.0 从入门到精通 ♦ 编 著 求是科技 责任编辑 张立科 ♦ 人民邮电出版社出版发行 北京市崇文区夕照寺街 14 号 邮编 100061 电子函件 315@ptpress.com.cn 网址 http://www.ptpress.com.cn 北京密云春雷印刷厂印刷 新华书店总店北京发行所经销 ♦ 开本:787×1092 1/16 印张:35.25 字数:855 千字 2006 年 3 月第 1 版 印数:1 – 5 000 册 2006 年 3 月北京第 1 次印刷 ISBN 7-115-14327-7/TP·5185 定价:54.00 元(附光盘) 读者服务热线:(010)67132692 印装质量热线:(010)67129223 前言 MATLAB 是 Mathworks 公司于 1984 年推出的一套高性能的数值计算和可视化软件,它 集数值分析、矩阵运算、信号处理和图形显示于一体,可方便地应用于数学计算、算法开发、 数据采集、系统建模和仿真、数据分析和可视化、科学和工程绘图、应用软件开发等方面。 MATLAB 之所以能够被广泛应用,是因为它将科研工作者从乏味的 Fortran、C 编程中解放出 来,使他们真正把精力放在科研和设计的核心问题上,从而大大提高了工作效率。在 MATLAB 环境中描述问题及编制求解问题的程序时,用户可以按照符合人们的科学思维方式和数学表 达习惯的语言形式来书写程序。 MATLAB 这个词代表“矩阵实验室”(matrix laboratory),它是以著名的线性代数软件包 LINPACK 和特征值计算软件包 EISPACK 中的子程序为基础,发展而成的一种开放型程序设 计语言。在它的发展过程中,许多优秀的工程师为它的完善做出了卓越的贡献,使其从一个 简单的矩阵分析软件逐渐发展成为一个具有极高通用性的、带有众多实用工具的运算操作平 台。工具箱是 MATLAB 函数的子程序库,每一个工具箱都是为某一类学科专业和应用而定 制的,主要包括信号处理、控制系统、神经网络、图像处理、模糊逻辑、小波分析和系统仿 真等方面的应用。借助于这些现有的工具,科研人员可以直观、方便地进行分析、计算及设 计工作,从而大大节省了时间。 本书主要是从使用方面对 MATLAB 7.0 进行了详细的介绍,大部分内容是通用的,也包 括一些专业性较强的章节,读者可以根据需要取舍。前五章介绍 MATLAB 的基础知识,包 括 MATLAB 的安装卸载及其系统功能的阐述,MATLAB 的数学运算和数据可视化工具以及 MATLAB 的编程等内容。接下来四章是 MATLAB 的高级应用部分,分别介绍了 MATLAB 7.0 的数据分析和处理功能,符号计算功能,Simulink 仿真环境和文件 I/O 操作。本书还对一些 应用广泛的工具箱(信号处理工具箱、图像处理工具箱和编译工具箱)单独进行了讲述,最 后介绍了高级图形设计的相关知识和 MATLAB 与 Word、Excel 的混合使用以及 MATLAB 的 应用程序接口。 为读者查阅方便,附录部分列出了 MATLAB 的常用命令和函数,Simulink 的库模块和 应用程序接口函数库。 本书叙述详细,深入浅出,又有丰富的例程,适合学习或使用 MATLAB 的本科生、研 究生和教师以及广大科研工作人员作为参考用书。除了篇幅和专业所限,有一些工具箱没有 介绍之外,MATLAB 系统的各个部分的功能都有比较详细的阐述,只要认真阅读,一定会有 收获。 由于时间仓促和作者的水平有限,书中难免有缺点和错误,敬请广大读者批评指正。欢 迎广大读者访问求是科技网站 http://www.cs-book.com,提出你的宝贵意见和建议,同时也欢 迎感兴趣的读者访问求是论坛做进一步交流。 编者 2006 年 2 月 目录 第 1 章 MATLAB 概述.................................................................................................................1 1.1 MATLAB 7.0 简介..........................................................................................................2 1.2 MATLAB 7.0 的安装、退出与卸载..............................................................................3 1.3 MATLAB 7.0 的目录结构..............................................................................................8 1.4 MATLAB 7.0 的工作环境..............................................................................................9 1.5 MATLAB 7.0 的通用命令............................................................................................16 1.6 使用 MATLAB 7.0 帮助系统.......................................................................................19 1.7 初识 MATLAB..............................................................................................................22 第 2 章 MATLAB 基础知识.......................................................................................................24 2.1 数据类型 .......................................................................................................................24 2.1.1 数值类型 ............................................................................................................24 2.1.2 逻辑类型 ............................................................................................................26 2.1.3 字符和字符串 ....................................................................................................26 2.1.4 函数句柄 ............................................................................................................26 2.1.5 结构体类型 ........................................................................................................27 2.1.6 单元数组类型 ....................................................................................................30 2.2 基本矩阵操作 ...............................................................................................................32 2.2.1 矩阵的构造 ........................................................................................................33 2.2.2 矩阵大小的改变 ................................................................................................35 2.2.3 矩阵下标引用 ....................................................................................................37 2.2.4 矩阵信息的获取 ................................................................................................39 2.2.5 矩阵结构的改变 ................................................................................................41 2.2.6 稀疏矩阵 ............................................................................................................42 2.3 运算符和特殊符号 .......................................................................................................46 2.3.1 算数运算符 ........................................................................................................46 2.3.2 关系运算符 ........................................................................................................48 2.3.3 逻辑运算符 ........................................................................................................49 2.3.4 运算优先级 ........................................................................................................50 2.4 字符串处理函数 ...........................................................................................................51 2.4.1 字符串的构造 ....................................................................................................51 2.4.2 字符串比较函数 ................................................................................................53 2.4.3 字符串查找和替换函数 ....................................................................................54 2.4.4 字符串——数值转换 ........................................................................................55 MATLAB 7.0 从入门到精通 第 3 章 数学运算 ........................................................................................................................57 3.1 矩阵运算 .......................................................................................................................57 3.1.1 矩阵分析 ............................................................................................................57 3.1.2 线性方程组 ........................................................................................................63 3.1.3 矩阵分解 ............................................................................................................67 3.1.4 矩阵的特征值和特征向量 ................................................................................74 3.1.5 非线性矩阵运算 ................................................................................................75 3.2 矩阵元素的数学函数 ...................................................................................................79 3.2.1 三角函数 ............................................................................................................79 3.2.2 指数和对数函数 ................................................................................................81 3.2.3 复数函数 ............................................................................................................81 3.2.4 截断和求余函数 ................................................................................................83 3.3 特殊数学函数 ...............................................................................................................84 3.3.1 特殊函数 ............................................................................................................84 3.3.2 数论函数 ............................................................................................................90 3.3.3 坐标变换函数 ....................................................................................................91 第 4 章 MATLAB 7.0 基本编程.................................................................................................92 4.1 脚本和函数 ...................................................................................................................92 4.1.1 函数 ....................................................................................................................92 4.1.2 脚本 ....................................................................................................................93 4.1.3 子函数与私有目录 ............................................................................................95 4.1.4 P 码文件.............................................................................................................95 4.2 MATLAB 中的变量和语句..........................................................................................96 4.2.1 变量类型 ............................................................................................................96 4.2.2 M 文件的流控制语句........................................................................................97 4.3 程序的调试(Debug) ..............................................................................................104 4.3.1 直接调试法 ......................................................................................................104 4.3.2 工具调试法 ......................................................................................................105 4.4 函数的设计和实现 ..................................................................................................... 115 4.4.1 建立数学模型 .................................................................................................. 115 4.4.2 编写代码 .......................................................................................................... 116 4.4.3 运行程序 ..........................................................................................................122 第 5 章 数据可视化 ..................................................................................................................128 5.1 二维绘图 .....................................................................................................................128 5.1.1 plot 命令...........................................................................................................128 5.1.2 fplot 命令..........................................................................................................131 5.1.3 ezplot 命令 .......................................................................................................133 5.2 三维绘图 .....................................................................................................................134 –2– 目录 5.2.1 plot3 命令.........................................................................................................134 5.2.2 mesh 命令.........................................................................................................135 5.2.3 surf 命令...........................................................................................................136 5.2.4 基本三维绘图命令的改进命令 ......................................................................137 5.3 特殊图形 .....................................................................................................................139 5.3.1 二维特殊图形函数 ..........................................................................................139 5.3.2 特殊的三维图形函数 ......................................................................................145 5.3.3 特殊坐标轴的图形函数 ..................................................................................147 5.3.4 四维表现图 ......................................................................................................152 5.4 图形处理 .....................................................................................................................153 5.4.1 图形标注 ..........................................................................................................153 5.4.2 坐标轴的控制 ..................................................................................................158 5.4.3 图形数据取点 ..................................................................................................161 5.4.4 子图和图形保持 ..............................................................................................162 5.4.5 色彩控制 ..........................................................................................................164 5.4.6 视角与光照 ......................................................................................................168 5.4.7 图形的打印和输出 ..........................................................................................175 5.5 图形窗口 .....................................................................................................................175 5.5.1 图形窗口的创建与控制 ..................................................................................175 5.5.2 图形窗口的菜单操作 ......................................................................................176 5.5.3 图形窗口的工具栏 ..........................................................................................184 第 6 章 数据分析 ......................................................................................................................186 6.1 多项式函数 .................................................................................................................186 6.1.1 多项式表示法 ..................................................................................................186 6.1.2 多项式求值 ......................................................................................................190 6.1.3 多项式乘法和多项式除法 ..............................................................................191 6.1.4 多项式的导数和微分 ......................................................................................192 6.1.5 多项式的根和由根创建多项式 ......................................................................194 6.1.6 多项式部分分式展开 ......................................................................................195 6.1.7 多项式曲线拟合 ..............................................................................................196 6.1.8 曲线拟合图形用户接口 ..................................................................................197 6.2 插值 .............................................................................................................................199 6.2.1 一维插值 ..........................................................................................................199 6.2.2 二维插值 ..........................................................................................................203 6.3 数据分析和傅立叶变换 .............................................................................................206 6.3.1 基本数据分析函数 ..........................................................................................206 6.3.2 协方差和相关系数矩阵 .................................................................................. 211 6.3.3 有限差分和梯度 ..............................................................................................213 6.3.4 信号滤波和卷积 ..............................................................................................214 –3– MATLAB 7.0 从入门到精通 6.3.5 傅立叶变换 ......................................................................................................218 6.4 功能函数 .....................................................................................................................223 6.4.1 函数的表示 ......................................................................................................223 6.4.2 函数画图 ..........................................................................................................224 6.4.3 函数最小值和零点 ..........................................................................................225 6.4.4 数值积分 ..........................................................................................................230 6.4.5 在功能函数中使用含参函数 ..........................................................................233 6.5 微分方程组数值解 .....................................................................................................235 6.5.1 常微分方程组的初值问题 ..............................................................................235 6.5.2 延迟微分方程组数值解 ..................................................................................242 6.5.3 常微分方程组的边界问题 ..............................................................................244 第 7 章 Simulink 仿真环境 ......................................................................................................248 7.1 Simulink 概述 .............................................................................................................248 7.1.1 Simulink 的概念 ..............................................................................................250 7.1.2 Simulink 的工作环境 ......................................................................................250 7.1.3 Simulink 的工作原理 ......................................................................................255 7.1.4 Simulink 模型的特点 ......................................................................................256 7.1.5 Simulink 里的数据类型...................................................................................257 7.1.6 Simulink 里的模块和模块库...........................................................................262 7.2 模型的创建 .................................................................................................................267 7.2.1 Simulink 模块的基本操作...............................................................................267 7.2.2 创建模型的基本步骤 ......................................................................................271 7.2.3 模型文件格式 ..................................................................................................272 7.3 子系统及其封装 .........................................................................................................273 7.3.1 创建子系统 ......................................................................................................273 7.3.2 封装子系统 ......................................................................................................275 7.3.3 定义自己的模块库 ..........................................................................................278 7.4 过零检测 .....................................................................................................................279 7.5 代数环 .........................................................................................................................280 7.6 回调函数 .....................................................................................................................282 7.7 运行仿真 .....................................................................................................................284 7.7.1 使用窗口运行仿真 ..........................................................................................284 7.7.2 仿真参数的设置 ..............................................................................................285 7.7.3 使用 MATLAB 命令运行仿真........................................................................289 7.7.4 改善仿真性能及精度 ......................................................................................290 7.8 仿真结果分析 .............................................................................................................291 7.8.1 观看输出结果 ..................................................................................................291 7.8.2 线性化 ..............................................................................................................292 7.8.3 平衡点的分析 ..................................................................................................293 –4– 目录 7.9 模型的调试 .................................................................................................................294 7.9.1 Simulink 调试器 ..............................................................................................294 7.9.2 命令行调试 ......................................................................................................296 7.9.3 设置断点 ..........................................................................................................296 7.9.4 显示仿真的有关信息 ......................................................................................297 7.9.5 显示模型的信息 ..............................................................................................299 7.10 S-函数 .......................................................................................................................299 7.10.1 什么是 S-函数................................................................................................300 7.10.2 为何要用 S-函数............................................................................................300 7.10.3 S-函数如何工作.............................................................................................300 7.10.4 怎样书写 S-函数............................................................................................302 7.10.5 S-函数应用示例.............................................................................................304 7.11 综合实例——PLL 中的非线性电荷泵和滤波器 ...................................................307 第 8 章 MATLAB 7.0 符号计算功能.......................................................................................313 8.1 符号运算入门 .............................................................................................................313 8.1.1 求解一元二次方程 x 2 + 2x + 2 = 0 的根 .....................................................313 8.1.2 求导数 d (cos2 x) ...........................................................................................314 dx b 8.1.3 计算定积分 ∫ x2dx ...........................................................................................314 a 8.1.4 求解一阶微分方程 dy = ay ............................................................................315 dt 8.2 符号对象的创建和使用 .............................................................................................315 8.2.1 创建符号对象和表达式 ..................................................................................315 8.2.2 符号对象的基本运算 ......................................................................................318 8.3 任意精度数学计算 .....................................................................................................319 8.4 符号表达式的化简和替换 .........................................................................................321 8.4.1 符号表达式的化简 ..........................................................................................321 8.4.2 符号表达式的替换 ..........................................................................................325 8.5 符号矩阵的计算 .........................................................................................................328 8.5.1 基本代数运算 ..................................................................................................328 8.5.2 线性代数运算 ..................................................................................................328 8.5.3 特征值分解 ......................................................................................................330 8.5.4 约当标准型 ......................................................................................................331 8.5.5 奇异值分解 ......................................................................................................331 8.6 符号微积分 .................................................................................................................332 8.6.1 符号表达式的微分运算 ..................................................................................333 –5– MATLAB 7.0 从入门到精通 8.6.2 符号表达式的极限 ..........................................................................................334 8.6.3 符号表达式的积分 ..........................................................................................335 8.6.4 级数的求和 ......................................................................................................336 8.6.5 泰勒级数 ..........................................................................................................336 8.7 符号积分变换 .............................................................................................................337 8.7.1 Fourier 变换 .....................................................................................................338 8.7.2 Laplace 变换 ....................................................................................................338 8.7.3 Z 变换...............................................................................................................339 8.8 符号方程求解 .............................................................................................................341 8.8.1 代数方程的求解 ..............................................................................................341 8.8.2 微分方程的求解 ..............................................................................................342 8.9 可视化数学分析界面 .................................................................................................343 8.9.1 图示化符号函数计算器 ..................................................................................343 8.9.2 泰勒级数逼近分析器 ......................................................................................345 8.10 Maple 接口................................................................................................................345 8.10.1 利用 sym 函数调用 Maple 函数 ...................................................................345 8.10.2 利用 maple 函数调用 Maple 函数 ................................................................346 第 9 章 文件 I/O........................................................................................................................347 9.1 打开和关闭文件 .........................................................................................................347 9.1.1 打开文件 ..........................................................................................................347 9.1.2 关闭文件 ..........................................................................................................348 9.2 读取二进制文件 .........................................................................................................349 9.3 写入二进制文件 .........................................................................................................351 9.4 读取文本文件 .............................................................................................................351 9.5 写入文本文件 .............................................................................................................354 9.6 文件内的位置控制 .....................................................................................................355 第 10 章 信号处理工具箱 ........................................................................................................358 10.1 数字信号处理基本理论 ...........................................................................................358 10.1.1 离散信号与系统 ............................................................................................358 10.1.2 Z 变换.............................................................................................................360 10.1.3 离散傅立叶变换 ............................................................................................361 10.1.4 数字滤波器结构 ............................................................................................363 10.2 MATLAB 7.0 的信号处理工具箱函数....................................................................368 10.2.1 波形产生(Waveform Generation).............................................................368 10.2.2 滤波器分析(Filter Analysis).....................................................................368 10.2.3 滤波器实现(Filter Implementation) .........................................................369 10.2.4 线性系统变换(Linear System Transformations) ......................................369 10.2.5 FIR 滤波器设计(FIR Digital Filter Design).............................................370 –6– 目录 10.2.6 IIR 滤波器设计(IIR Digital Filter Design)...............................................370 10.2.7 IIR 滤波器阶的选择(IIR Filter Order Estimation) ..................................370 10.2.8 变换(Transforms)......................................................................................371 10.2.9 统计信号处理和谱分析(Statistical Signal Processing and Spectral Analysis)371 10.2.10 窗函数(Windows) ...................................................................................372 10.2.11 参数化建模(Parametric Modeling).........................................................372 10.2.12 特殊操作(Specialized Operations) .........................................................373 10.2.13 模拟低通滤波器原型(Analog Lowpass Filter Prototypes)....................373 10.2.14 模拟滤波器设计(Analog Filter Design).................................................373 10.2.15 模拟滤波器转换(Analog Filter Transformation) ...................................374 10.2.16 滤波器离散化(Filter Discretization) ......................................................374 10.2.17 对数倒谱分析(Cepstral Analysis) ..........................................................374 10.2.18 线性预测(Linear Prediction) ..................................................................374 10.2.19 多速信号处理(Multirate Signal Processing) ..........................................375 10.2.20 图形用户接口(Graphical User Interfaces) .............................................375 10.3 基于 MATLAB 的信号处理系统分析与设计.........................................................375 10.3.1 离散信号与系统的 MATLAB 实现..............................................................375 10.3.2 离散傅立叶变换的 MATLAB 实现..............................................................378 10.3.3 Z 变换的 MATLAB 实现 ..............................................................................380 10.3.4 FIR 滤波器的 MATLAB 实现.......................................................................383 10.3.5 IIR 滤波器的 MATLAB 实现 .......................................................................386 第 11 章 图像处理工具箱 ........................................................................................................391 11.1 图像处理工具箱介绍 ...............................................................................................391 11.1.1 常用图像格式 ................................................................................................391 11.1.2 MATLAB 7.0 图像类型 .................................................................................393 11.1.3 MATLAB 7.0 图像类型转换 .........................................................................396 11.2 图像的显示 ...............................................................................................................398 11.2.1 标准图像显示技术 ........................................................................................398 11.2.2 特殊图像显示技术 ........................................................................................400 11.3 图像的几何运算 .......................................................................................................403 11.3.1 图像插值 ........................................................................................................403 11.3.2 图像大小调整 ................................................................................................404 11.3.3 图像旋转 ........................................................................................................405 11.3.4 图像剪裁 ........................................................................................................406 11.4 图像的变换技术 .......................................................................................................407 11.4.1 数字图像的二维傅立叶变换 ........................................................................408 11.4.2 数字图像的离散余弦变换 ............................................................................412 11.4.3 其他变换技术 ................................................................................................417 11.5 图像分析 ...................................................................................................................418 –7– MATLAB 7.0 从入门到精通 11.5.1 像素值及其统计 ............................................................................................419 11.5.2 图像分析 ........................................................................................................422 11.5.3 图像调整 ........................................................................................................425 11.5.4 图像平滑 ........................................................................................................428 11.6 特殊区域处理 ...........................................................................................................431 11.6.1 区域的指定 ....................................................................................................431 11.6.2 特定区域滤波 ................................................................................................432 11.6.3 特定区域填充 ................................................................................................433 第 12 章 高级图形设计 ............................................................................................................435 12.1 句柄图形 ...................................................................................................................435 12.1.1 图形对象、图像句柄和句柄图形树结构 ....................................................435 12.1.2 图形对象种类 ................................................................................................436 12.1.3 图形对象属性概念 ........................................................................................438 12.2 图形对象的操作 .......................................................................................................439 12.2.1 创建图形对象 ................................................................................................439 12.2.2 图形对象属性设置 ........................................................................................441 12.2.3 属性值查询 ....................................................................................................442 12.2.4 设置用户属性默认值 ....................................................................................444 12.3 句柄使用方法 ...........................................................................................................448 12.3.1 访问对象句柄 ................................................................................................448 12.3.2 使用句柄操作图形对象 ................................................................................450 12.3.3 控制图形输出 ................................................................................................452 12.3.4 在 M 文件中保存句柄...................................................................................456 12.4 GUI 设计向导 ...........................................................................................................458 12.4.1 图形用户界面概述 ........................................................................................458 12.4.2 启动 GUIDE...................................................................................................459 12.4.3 GUIDE 提供的用户控件...............................................................................460 12.4.4 界面设计工具集 ............................................................................................461 12.4.5 GUI 组态 ........................................................................................................467 12.4.6 GUI 界面设计 ................................................................................................470 12.5 编程设计 GUI ...........................................................................................................472 12.5.1 M 文件以及 GUI 数据管理...........................................................................472 12.5.2 回调函数的使用方法 ....................................................................................474 12.5.3 图形窗口的行为控制 ....................................................................................476 12.6 图形用户界面设计实例 ...........................................................................................477 12.6.1 图形界面的实现 ............................................................................................477 12.6.2 行为控制的实现 ............................................................................................478 –8– 目录 第 13 章 MATLAB 7.0 与 Word、Excel 的混合使用 .............................................................487 13.1 Notebook 的安装和使用环境 ..................................................................................487 13.2 一个 Notebook 实例 .................................................................................................488 13.3 Notebook 使用的几个问题 ......................................................................................490 13.4 Excel link 的安装和使用环境..................................................................................491 13.4.1 Excel link 的安装...........................................................................................491 13.4.2 设置 Excel link 的启动方式 ..........................................................................493 13.4.3 终止 Excel link 的运行..................................................................................494 13.5 一个 Excel link 实例.................................................................................................495 13.5.1 数据表执行方式 ............................................................................................495 13.5.2 宏命令执行模式(Macro Version).............................................................496 13.6 Excel link 使用的几个问题......................................................................................498 13.6.1 关于语法 ........................................................................................................499 13.6.2 关于工作表 ....................................................................................................499 第 14 章 编译工具箱 ................................................................................................................500 14.1 编译器概述 ...............................................................................................................500 14.2 编译器的安装和配置 ...............................................................................................500 14.2.1 配置 MATLAB 7.0 编译器的前提准备 ........................................................500 14.2.2 对编译器进行配置 ........................................................................................501 14.3 MATLAB 7.0 编译器的使用....................................................................................503 14.3.1 编译过程 ........................................................................................................503 14.3.2 MCR 的安装 ..................................................................................................503 14.3.3 编译指令 mcc ................................................................................................504 14.3.4 创建独立的应用程序 ....................................................................................505 第 15 章 应用程序接口 ............................................................................................................ 511 15.1 创建 C 语言 MEX 文件............................................................................................ 511 15.1.1 MEX 文件简介 .............................................................................................. 511 15.1.2 编写 C MEX 文件..........................................................................................512 15.2 创建 Fortran 语言 MEX 文件...................................................................................514 15.2.1 Fortran 语言 MEX 文件简介.........................................................................514 15.2.2 Fortran MEX 文件示例..................................................................................515 15.3 MAT 文件应用..........................................................................................................516 15.4 MATLAB 引擎技术的应用......................................................................................519 15.5 MATLAB 的 Java 接口.............................................................................................522 15.5.1 Java 接口应用 ................................................................................................522 15.5.2 应用示例 ........................................................................................................524 15.6 MATLAB 中的 DDE 技术 .......................................................................................525 –9– MATLAB 7.0 从入门到精通 15.6.1 关于 DDE 的一般性说明 ..............................................................................525 15.6.2 DDE 中的 MATLAB 服务器.........................................................................526 15.6.3 DDE 中的 MATLAB 客户.............................................................................526 15.7 MATLAB 中的 ActiveX 技术 ..................................................................................529 15.7.1 关于 ActiveX 的一般性说明.........................................................................529 15.7.2 MATLAB 的 ActiveX 自动化 .......................................................................529 附录 .............................................................................................................................................. 532 A.1 常用命令和函数 ........................................................................................................532 A.2 SIMULINK 的库模块................................................................................................543 A.2.1 库模块 .............................................................................................................543 A.2.2 连续模块子库 Continuous..............................................................................543 A.2.3 离散模块子库 Discrete ...................................................................................544 A.2.4 解析函数和查表函数模块子库 Functions&Tables .......................................544 A.2.5 一般数学函数子库 Math................................................................................544 A.2.6 非线性模块子库 Nonlinear ............................................................................544 A.2.7 信号和系统模块子库 Signal&Systems..........................................................545 A.2.8 信宿模块子库 Sinks .......................................................................................545 A.2.9 信源模块子库 Sources....................................................................................545 A.3 应用程序接口函数库 ................................................................................................546 A.3.1 外部程序接口函数库 .....................................................................................546 A.3.2 MAT 文件库函数............................................................................................546 A.3.3 MATLAB 引擎函数库....................................................................................547 A.3.4 ActiveX 对象的构造和操作命令...................................................................547 A.3.5 动态数据交换函数 .........................................................................................547 –10– 第 1 章 MATLAB 概述 MATLAB 是一种高效的工程计算语言,它将计算、可视化和编程等功能集于一个易于使 用的环境。在 MATLAB 环境中描述问题及编制求解问题的程序时,用户可以按照符合人们 科学思维的方式和数学表达习惯的语言形式来书写程序。其典型应用主要包括以下几个方面: • 数学计算; • 算法开发; • 数据采集; • 系统建模和仿真; • 数据分析和可视化; • 科学和工程绘图; • 应用软件开发(包括用户界面)。 MATLAB 是一个交互式系统(写程序与执行命令同步),其基本的数据元素是没有维数 限制的阵列。这使得用户可以解决许多工程技术上的问题,特别是那些包含了矩阵和向量的 公式的计算。采用 MATLAB 编制解决上述问题的程序比采用只支持标量和非交互式的编程 语言(如 C 语言和 Fortan 语言)更加方便。 MATLAB 这个词代表“矩阵实验室”(matrix laboratory),它是以线性代数软件包 LINPACK 和特征值计算软件包 EISPACK 中的子程序为基础发展起来的一种开放型程序设计语言。20 世 纪 80 年代初期,Cleve Moler 和 John Little 采用 C 语言改写了 MATLAB 的内核,不久他们便 成立了 Mathworks 软件开发公司,并将 MATLAB 正式推向市场。历经十几年的发展和竞争, MATLAB 成为国际认可的最优化的科技应用软件。在大学里,它是用于初等和高等数学、自 然科学和工程学的标准教学工具;在工业界,它是一个高效的研究、开发和分析的工具。随着 科技的发展,许多优秀的工程师不断地对 MATLAB 进行了完善,使其从一个简单的矩阵分析 软件逐渐发展成为一个具有极高通用性,并带有众多实用工具的运算操作平台。 MATLAB 的一个重要特色就是它有一套程序扩展系统和一组称之为工具箱(toolboxes) 的特殊应用子程序。工具箱是 MATLAB 函数的子程序库,每一个工具箱都是为某一类学科 专业和应用而定制的,主要包括信号处理、控制系统、神经网络、模糊逻辑、小波分析和系 统仿真等方面的应用。 MATLAB 系统由以下 5 个主要部分组成,下面具体进行介绍。 • 开发环境:由一系列工具组成。这些工具方便用户使用 MATLAB 的函数和文件,其 中许多工具采用的是图形用户界面。包括 MATLAB 桌面和命令窗口、历史命令窗口、 编辑器和调试器、路径搜索和用于浏览帮助、工作空间、文件的浏览器。 • MATLAB 数学函数库:这是一个包含大量计算算法的集合,这些函数包括从最简单最 基本的函数(如加、正弦等)到诸如矩阵的特征向量、快速傅立叶变换等较复杂的函数。 • MATLAB 语言:这是一个高级的矩阵/阵列语言,它包含控制语句、函数、数据结构、 输入输出和面向对象的编程特点。用户可以在命令窗口中将输入语句与执行命令同步, MATLAB 7.0 从入门到精通 也可以先编写好一个较大的复杂的应用程序(M 文件)后再一起运行。 • 图形处理:用 MATLAB 可以将向量和矩阵用图形表现出来,并且可以对图形进行标注 和打印。高层次的作图包括二维和三维数据可视化、图像处理、动画和表达式作图,低 层次的作图包括定制图形的显示和为用户的 MATLAB 应用程序建立的图形用户界面。 • MATLAB 应用程序接口(API):这是一个库,它允许用户编写可以和 MATLAB 进行 交互的 C 或 Fortran 语言程序。 1.1 MATLAB 7.0 简介 MATLAB 软件从 1984 年推出的第 1 个版本到目前发布的第 14 个版本 MATLAB 7.0 (Release14),有了较大的改进和增补,增加了许多新功能和更为有效的处理方法。 1.开发环境 • 新的用户界面环境和开发环境,使用户更方便地控制多个文件和图形窗口,用户可以 按照自己的习惯来定制桌面环境,还可以为常用的命令定义快捷键; • 功能更强的数组编辑器和工作空间浏览器,用户可更方便地浏览、编辑和图形化变量; • 提供的 M-Lint 代码分析器,可以方便用户修改代码以取得更好的性能和可维护性; • 更强大的编辑器,用户可以选择执行 M 文件中的部分内容等。 2.编程 • 支持函数嵌套、有条件中断点; • 可以用匿名函数来定义单行函数等。 3.数值处理 • 整数算法,方便用户处理更大的整数; • 单精度算法、线性代数、FFT 和滤波,方便用户处理更大的单精度数据; • Linsolve 函数,用户可以通过定义系数矩阵更快地求解线性系统; • ODE 求解泛函数,操作隐式差分等式和求解多点式边界值问题。 4.图形化 • 新的绘图界面窗口,用户可以不必通过输入 M 函数代码而直接在绘图界面窗口中交互 式地创建并编辑图形; • 用户可以直接从图形窗口中生成 M 代码文件,使得用户可以多次重复地执行用户自定 义的作图; • 更强大的图形标注和处理功能,包括对象对齐、连接注释和数据点的箭头等; • 数据探测工具,用户可以在图形窗口中方便地查询图形上某一点的坐标值; • 功能更强大的图形句柄等。 5.图形用户界面 • 面板和分组按钮使得用户可以对用户界面的控件进行分组; –2– • 用户可以直接在 GUIDE 中访问 ActiveX 控件。 第 1 章 MATLAB 概述 6.文件 I/O 和外部应用程序接口 • 新的文件 I/O 函数支持用户可以读更大的文本文件,并且可以向 Excel 和 HDF5 文件 中写入内容; • 支持压缩格式的 MAT 文件,使得用户可以使用较少的磁盘空间保存大量的数据,而 且速度更快; • 可以使用 Javaaddpath 函数来动态添加、删除或重载 Java 类,而不必重启 MATLAB 7.0; • 支持 COM 用户接口、服务器事件和 Visual Basic 脚本; • 可以基于简单的对象访问协议(SOAP)来访问网页服务器; • 提供 FTP 对象用于连接 FTP 服务器,实现对异地文件的处理; • 支持 Unicode 国际字符集标准,使得 MAT 文件中的字符数据可以在不同语言之间共享。 1.2 MATLAB 7.0 的安装、退出与卸载 1.对硬件和软件的要求 MATLAB 7.0(Release 14)可以安装到下列操作平台上: • Windows 2000(Service Pack 3 或 4); • Windows NT 4.0(Service Pack 5 或 6a); • Windows XP; • Linux ix86 2.4.x, glibc 2.2.5; • Sun Solaris 2.8 和 2.9; • HPUX 11.0 和 11.1; • Mac OS X 10.3.2。 无 论 在 单 机 还 是 网 络 环 境 , MATLAB 都 可 发 挥 其 卓 越 的 性 能 。 若 单 纯 地 使 用 MATLAB 语言进行编程,而不必连接外部语言的程序,则 MATLAB 语言编写出来的程 序可以不做任何修改直接移植到其他机型上去使用。MATLAB 7.0 对 PC 机系统的要求 如表 1-1 所示。 表 1-1 操作平台 处理器 存储空间 内存 显卡 软件 MATLAB 7.0 对系统的要求 Windows XP、Windows 2000 (Service Pack 3 or 4)、Windows NT 4.0 (Service Pack 5 or 6a) Pentium III、4、Xeon、 Pentium M、AMD Athlon、Athlon XP、Athlon MP 345 MB (仅包括帮助系统的 MATLAB ) 256 MB (最小),512 MB (推荐) 16-bit、24-bit 或 32-bit 兼容 OpenGL 的图形适配卡 (强烈推荐) 图形加速卡、打印机、声卡 为了运行 MATLAB Notebook、MATLAB Builder for Excel、Excel Link、Database Toolbox、 and MATLAB Web Server,还必须安装 Office 2000 或 Office XP –3– MATLAB 7.0 从入门到精通 编译器 续表 为了创建自己的 MEX 文件,则至少需要下列产品之一:DEC Visual Fortran 5.0、 Microsoft Visual C/C++4.2 或 5.0、Borland C/C++5.0 或 5.02 Watcom 10.6 或 11 2.安装过程 随着 MATLAB 版本的更新,安装也越来越简便。对于 MATLAB 7.0,用户只要按照安装 界面的提示逐步进行即可。下面介绍在 Windows 系统下的安装过程。 • Step 1:安装前的准备 准备好安装密码(PLP);退出正在运行的其他版本的 MATLAB;确保系统满足安装的 要求;获得系统用户的许可权;最好不要在安装过程中运行病毒扫描程序,因为这样会降低 安装速度。 • Step 2:开始安装 插入 MATLAB 7.0 光盘到光驱,在 MATLAB 7.0 目录下直接运行“Setup.exe”程序,显 示初始化画面,随之显示准备安装的进度条。一般情况下系统会自动搜索到 autorun 文件并 进入安装界面。随后出现“Welcome to the MathWorks Installer”对话框,如图 1-1 所示。 图 1-1 “Welcome to the MathWorks Installer”对话框 图 1-2 “License Information”对话框 • Step 3:输入用户信息 选择图 1-1 中的“Next”继续安装,出现“License Information”对话框,如图 1-2 所示。 在对话框界面的相应位置输入相应的内容,然后单击“下一步”。 • Step 4:浏览如图 1-3 所示的软件许可协议(License Agreement) –4– 图 1-3 “License Agreement”对话框 第 1 章 MATLAB 概述 • Step 5:选择安装类型 用户可以在如图 1-4 所示的“InstallactionType”对话框中选择安装类型,典型(Typical) 或自定义(Custom)。 图 1-4 “InstallActionType”对话框 这里选择的是典型安装类型,这样可以简化安装过程,不过安装后会有如下的限制: ① 拥有个人许可证; ② 不能自由地有选择性地安装所需要的产品; ③ 不需要访问安装选项,例如设置某些文件的访问权限等。 如果选择自定义安装类型,可以允许用户选择所要安装的产品,并设定哪些需要访问安 装选项。为了保证使用所有产品的各项功能并简化安装过程,一般选择典型安装类型,然后 选择进入下一步。 • Step 6:定义安装的目录和所要安装的产品 如果用户选择的是典型安装类型,则只需要设置安装目录;如果选择的是自定义安装类 型,则除了设置安装目录外还要选择所需安装的产品。图 1-5 所示为典型安装类型下定义安 装目录的界面,图 1-6 是自定义安装类型的设置界面。本例以典型安装类型为例介绍其安装 方法。 图 1-5 典型安装类型下定义安装目录 • Step 7:确认前面设置的安装目录 图 1-6 自定义安装类型下定义安装目录和选择产品 –5– MATLAB 7.0 从入门到精通 图 1-7 确认安装设置 图 1-8 换安装光盘 单击“Install”安装按钮就可以进行安装,并显示一个用以表示当前安装进度的对话框。 如果用户所要安装的产品不在当前的光盘上,则会弹出如图 1-8 所示的提示对话框。用户只 要按照提示插入另一张光盘即可继续安装。 • Step 8:阅读产品配置报告 图 1-9 阅览安装情况 • Step 9:完成安装 当安装完毕时,会自动弹出 Setup Complete 对话框。在该对话框中,用户可以选择在退 出安装后自动启动 MATLAB 7.0,如果用户不希望退出安装后直接启动它,可以取消选择框。 单击“Finish”按钮结束安装。 –6– 图 1-10 结束安装 第 1 章 MATLAB 概述 安装完成后,用户可以进行以下工作。 (1)运行 MATLAB 7.0 有 3 种运行 MATLAB 7.0 的方式。用鼠标左键双击桌面的 MATLAB 图标 ;单击 Start 按钮,选择 Programs,然后在打开的如图 1-11 所示的菜单中选择 MATLAB 7.0;使用 Windows 浏览器打开 MATLAB 7.0 的顶层安装目录,双击快捷运行图标。 图 1-11 通过开始菜单运行 MATLAB 7.0 (2)设置初始目录 在默认情况下,当用户开始运行 MATLAB 7.0 后,默认的初始目录是“$MATLAB\work”。 其中“$MATLAB”是指 MATLAB 7.0 的安装路径,work 目录是用于存放修改过的和用户创 建的 M-文件,当用户卸载 MATLAB 7.0 后,该目录依然被保存。 用户还可以修改初始的当前目录,用鼠标右键单击桌面上的 MATLAB 7.0 快捷图标,选 择 Properties 选项,在“起始位置”的文本区定义自己的初始目录,如图 1-12 所示。 图 1-12 设置初始工作目录 (3)建立 MATLAB 7.0 环境选项 每次在启动 MATLAB 7.0 时,用户可以自定义所要执行的任务,包括欢迎消息、默认定 义或任何 MATLAB 表达式。将所有这些任务写在“$MATLAB\toolbox\local”目录下的名称 为 startup.m 的文件中,用户每一次启动 MATLAB 7.0 时,系统都会自动执行该文件中的语句。 (4)配置产品 在前面介绍的已安装的某些产品还需要附加配置才能正常使用,在表 1-2 中列出了这些 产品以及对它们进行配置的命令,命令的详细使用方法请用户查阅帮助。 表 1-2 产品 MATLAB Notebook Real-Time Windows Target 配置产品 notebook -setup rtwintgt -setup 配置命令 –7– MATLAB 7.0 从入门到精通 (5)查询有关 MATLAB 7.0 的信息 表 1-3 列出了查询 MATLAB 7.0 相应信息的方法。 表 1-3 查询 MATLAB 7.0 信息 任务 描述 获得对 MATLAB 7.0 总的认识和功能 阅读 MATLAB 7.0 Getting Started 文件 查询所安装版本的新特性 阅读 Release Notes 文件 启动某一产品或访问产品的示例程序 使用桌面的 Start 按钮 获得 MATLAB 7.0 某一特性的相关信息 选择 MATLAB 7.0 主界面的 Help 菜单的相应选项获得相关的 HTML 格式的帮助文件 查询不能在文件中看到的帮助主题 访问 MathWorks 网址(www.mathworks.com) 3.退出 MATLAB 7.0 前面已经介绍了启动 MATLAB 7.0 的方法,下面介绍几种退出 MATLAB 7.0 的方法。 • 在 MATLAB 7.0 命令窗口的“File”菜单下选择“Exit MATLAB”选项; • 快捷键“Ctrl+q”; • 在命令窗口输入“quit”; • 在命令窗口输入“exit”; • 用鼠标单击 MATLAB 7.0 命令窗口右上角的“ ”按钮; • 用鼠标双击 MATLAB 7.0 命令窗口左上角的图标。 4.卸载 MATLAB 7.0 当用户需要卸载 MATLAB 7.0 时,单击系统开始菜单,选择“程序→MATLAB 7.0→R14 Uninstaller”,在弹出的如图 1-13 所示的对话框中选择所要卸载的产品,然后单击“Uninstall” 按钮,开始执行卸载命令。 图 1-13 卸载 MATLAB 7.0 1.3 MATLAB 7.0 的目录结构 当用户成功安装 MATLAB 7.0 后,在用户所选择的安装目录下将包含如表 1-4 所示的文 件夹目录。 –8– 表 1-4 文件夹 \BIN\WIN32 \DEMOS \EXTERN \HELP \JA \JAVA \NOTEBOOK \SYS \TOOLBOX \UNINSTALL \WORK RTW SIMULINK STATEFLOW License.txt 第 1 章 MATLAB 概述 MATLAB 7.0 的目录结构 描述 MATLAB 7.0 系统中可执行的相关文件 MATLAB 7.0 示例程序 创建 MATLAB 7.0 的外部程序接口的工具 帮助系统 MATLAB 7.0 国际化文件 MATLAB 7.0 的 Java 支持程序 Notebook 是用来实现 MATLAB 数学工作环境与 Word 字处理环境信息交 互的软件,是一个兼备数学计算、图形显示和文字处理能力的集成环境 MATLAB 7.0 所需要的工具和操作系统库 MATLAB 7.0 的各种工具箱 MATLAB 7.0 的卸载程序 默认的当前目录 Real-Time Workshop 软件包 Simulink 软件包,用于动态系统的建模、仿真和分析 Stateflow 软件包,用于状态机设计的功能强大的图形化开发和设计工具 该文件为软件许可协议的内容 1.4 MATLAB 7.0 的工作环境 本节通过介绍 MATLAB 7.0 工作环境界面,使读者初步掌握 MATLAB 7.0 软件的基本操 作方法。 MATLAB 7.0 的工作界面主要由菜单、工具栏、当前工作目录窗口、工作空间管理窗口、 历史命令窗口和命令窗口组成,如图 1-14 所示。 图 1-14 MATLAB 7.0 工作环境 –9– MATLAB 7.0 从入门到精通 1.菜单和工具栏 MATLAB 7.0 的菜单和工具栏界面与 Windows 程序的界面类似,用户只要稍加实践就可 掌握其功能和使用方法。菜单的内容会随着在命令窗口中执行不同命令而作出相应改变。这 里只简单介绍默认情况下的菜单和工具栏。 【File】菜单 • Import Data:用于向工作空间导入数据; • Save Workspace As:将工作空间的变量存储在某一文件中; • Set path:打开搜索路径设置对话框; • Preferences:打开环境设置对话框(如图 1-15 所示)。 图 1-15 Preferences 设置对话框 【Edit】菜单 主要用于复制、粘贴等操作,与一般的 Windows 程序的类似,在此不作详细介绍。 【Debug】菜单 用于设置程序的调试。 【Desktop】菜单 用于设置主窗口中需要打开的窗口。 【Window】菜单 列出当前所有打开的窗口。 【Help】菜单 用于选择打开不同的帮助系统。 当用户单击“Current Directory”窗口时,使得该窗口成为当前窗口,那么会增加一个如 图 1-16 所示的菜单【View】,用于设置如何显示当前目录下的文件。 –10– 第 1 章 MATLAB 概述 图 1-16 View 菜单 当用户单击“Workspace”窗口时,使得该窗口成为当前窗口,那么会增加如图 1-17 所 示的菜单【View】和【Graphics】。菜单【View】用于设置如何在工作空间管理窗口中显示变 量,菜单【Graphics】用于打开绘图的工具,用户可以使用这些工具来绘制变量。 图 1-17 Graphics 菜单 下面介绍“工具栏”中部分按钮的功能。 :打开 Simulink 主窗口。 :打开用户界面设计窗口。 :打开帮助系统。 :设置当前目录。 –11– MATLAB 7.0 从入门到精通 单击主窗口左下脚的【Start】开始按钮,可以直接打开各种 MATLAB 7.0 工具,如图 1-18 所示。 图 1-18 开始按钮 2.命令窗口 MATLAB 7.0 的命令窗口如图 1-19 所示,其中“>>”为运算提示符,表示 MATLAB 处 于准备状态。当在提示符后输入一段程序或一段运算式后按【Enter】键,MATLAB 会给出 计算结果,并再次进入准备状态(所得结果将被保存在工作空间管理窗口中)。 图 1-19 命令窗口 单击命令窗口右上角的 按钮,可以使命令窗口脱离主窗口而成为一个独立的窗口,如 图 1-20 所示。 –12– 图 1-20 独立的命令窗口 第 1 章 MATLAB 概述 在该窗口中选中某一表达式,然后单击鼠标右键,弹出如图 1-21 所示的上下文菜单,通 过不同的选项可以对选中的表达式进行相应的操作。 图 1-21 命令窗口的上下文菜单 3.历史命令窗口 该窗口主要用于记录所有执行过的命令,在默认设置下,该窗口会保留自安装后所有使 用过的命令的历史记录,并标明使用时间。同时,用户可以通过用鼠标双击某一历史命令来 重新执行该命令。与命令窗口类似,该窗口也可以成为一个独立的窗口。 在该窗口中选中,然后单击鼠标右键,弹出如图 1-22 所示的上下文菜单。通过上下文菜 单,用户可以删除或粘贴历史记录;也可为选中的表达式或命令创建一个 M-文件;还可为某 一句或某一段表达式或命令创建快捷按钮,具体方法见下面的示例。 图 1-22 历史命令窗口的上下文菜单 • 选择图 1-22 中的“Create Shortcut”菜单项,弹出如图 1-23 所示的“快捷键设置”对 话框。 图 1-23 “快捷键设置”对话框 –13– MATLAB 7.0 从入门到精通 • 按照图 1-23 进行快捷键的设置,然后单击“Save”按钮,注意观察工具栏 Shortcuts 栏的变化,如图 1-24 所示。 图 1-24 创建新的快捷键 • 用鼠标左键单击新加入的快捷按钮,命令窗口中会显示相应命令执行的结果。 ans = 3.6739e-016 >> 用户还可以直接按住鼠标左键不放,将所选中的历史命令直接拖到 Shortcuts 栏中,这样 也可为所选命令创建快捷键。 4.当前工作目录窗口 在目录窗口中可显示或改变当前目录,还可以显示当前目录下的文件,以及搜索功能。 与命令窗口类似,该窗口也可以成为一个独立的窗口,如图 1-25 所示。 –14– 图 1-25 当前目录窗口 第 1 章 MATLAB 概述 下面介绍“当前工作目录窗口”中部分按钮的功能。 :显示并改变当前目录; :进入所显示目录的上一级目录; :在当前目录中创建一个新的子目录; :在当前目录中查找一个文件; :在图 1-25 中选中该按钮后,当前目录中的文件即以类的形式显示,如图 1-26 所示。 :单击该按钮后即可生成一个当前目录中的 M-文件的报告文件,如图 1-27 所示。如 果选择该栏下“Contents Report”选项可生成不同的报告文件,如图 1-28 所示。 图 1-26 以类的形式显示当前目录中的文件 图 1-27 当前目录中 M-文件的报告文件 图 1-28 生成不同的报告文件 5.工作空间管理窗口 在工作空间管理窗口中将显示目前内存中所有的 MATLAB 变量的变量名、数据结构、 字节数以及类型等信息,不同的变量类型分别对应不同的变量名图标,如图 1-29 所示。 图 1-29 工作空间管理窗口 –15– MATLAB 7.0 从入门到精通 下面介绍“工作空间管理窗口”中部分按钮的功能。 :向工作空间添加新的变量; :打开在工作空间中选中的变量; :向工作空间中导入数据文件; :保存工作空间中的变量; :删除工作空间中的变量; :绘制工作空间中的变量,可以用不同的绘制命令来绘制变量,如图 1-30 所示。 图 1-30 不同的绘制变量命令 1.5 MATLAB 7.0 的通用命令 通用命令是 MATLAB 7.0 中经常使用的一组命令,这些命令可以用来管理目录、命令、 函数、变量、工作空间、文件和窗口。为了更好地使用 MATLAB 7.0,用户需要熟练掌握和 理解这些命令。下面对这些命令进行介绍。 1.常用命令 常用命令的功能如表 1-5 所示。 表 1-5 命令 cd dir clc home clf type clear echo disp 常用命令 命令说明 命令 显示或改变当前工作目录 load 显示当前目录或指定目录下的文件 diary 清除工作窗中的所有显示内容 ! 将光标移至命令窗口的最左上角 exit 清除图形窗口 quit 显示文件内容 pack 清理内存变量 hold 工作窗信息显示开关 path 显示变量或文字内容 save 命令说明 加载指定文件的变量 日志文件命令 调用 DOS 命令 退出 MATLAB 7.0 退出 MATLAB 7.0 收集内存碎片 图形保持开关 显示搜索目录 保存内存变量到指定文件 2.输入内容的编辑 在 MATLAB 7.0 命令窗口中,为了便于对输入的内容进行编辑,MATLAB 7.0 提供了一 –16– 第 1 章 MATLAB 概述 些控制光标位置和进行简单编辑的一些常用编辑键和组合键,掌握这些可以在输入命令的过 程中起到事半功倍的效果。表 1-6 列出了一些常用键盘按键及其作用。 表 1-6 键盘按键 说明 命令行中的键盘按键 键盘按键 说明 ↑ Ctrl+p,调用上一行 home Ctrl+a,光标置于当前行开头 ↓ Ctrl+n,调用下一行 end Ctrl+e,光标置于当前行末尾 ← Ctrl+b,光标左移一个字符 esc Ctrl+u,清除当前输入行 → Ctrl+f,光标右移一个字符 del Ctrl+d,删除光标处的字符 Ctrl+← Ctrl+l,光标左移一个单词 backspace Ctrl+h,删除光标前的字符 Ctrl+→ Ctrl+r,光标右移一个单词 Alt+backspace 恢复上一次删除 3.标点 在 MATLAB 语言中,一些标点符号也被赋予了特殊的意义,或代表一定的运算,具体 内容如表 1-7 所示。 表 1-7 标点 : ; , () [] {} 说明 MATLAB 语言的标点 标点 冒号,具有多种应用功能 % 分号,区分行及取消运行结果显示 ! 逗号,区分列及函数参数分隔符 = 括号,指定运算的优先级 ‘ 方括号,定义矩阵 . 大括号,构造单元数组 … 说明 百分号,注释标记 惊叹号,调用操作系统运算 等号,赋值标记 单引号,字符串的标示符 小数点及对象域访问 续行符号 4.搜索路径与扩展 当 MATLAB 7.0 对函数或文件等进行搜索时,都是在其搜索路径下进行的。如果用户调 用的函数在搜索路径之外,MATLAB 7.0 则认为此函数并不存在。一般情况下,MATLAB 7.0 系统的函数(包括工具箱函数)都在系统默认的搜索路径之中,但是用户自己书写的函数有 可能并没有保存在搜索路径下。要解决这个问题,只需把程序所在的目录扩展成 MATLAB 7.0 的搜索路径即可。下面就向读者介绍设置 MATLAB 7.0 搜索路径及其扩展的方法。 (1)查看 MATLAB 7.0 的搜索路径 可以通过菜单命令和 path、genpath 命令函数两种方法来查看搜索路径。 选择 MATLAB 7.0 主窗口中的“File→Set Path”菜单,进入“设置搜索路径”对话框, 如图 1-31 所示。通过该对话框可为 MATLAB 添加或删除搜索路径,用户只要稍加练习就可 掌握。 –17– MATLAB 7.0 从入门到精通 图 1-31 “设置搜索路径”对话框 在命令窗口中输入命令 path 或 genpath 可得到 MATLAB 7.0 的所有搜索路径,代码如下: >> path MATLABPATH E:\MATLAB H:\MATLAB7\toolbox\MATLAB\general H:\MATLAB7\toolbox\MATLAB\ops H:\MATLAB7\toolbox\MATLAB\lang … H:\MATLAB7\work >>genpath ans = H:\MATLAB7\toolbox;H:\MATLAB7\toolbox\aeroblks;H:\MATLAB7\toolbox\aeroblks\aeroblks;H:\MATLA B7\toolbox\aeroblks\aeroblks\ja;H:\MATLAB7\toolbox\aeroblks\aerodemos;H:\MATLAB7\toolbox\aeroblks\aerod emos\texture;H:\MATLAB7\toolbox\bioinfo;H:\MATLAB7\toolbox\bioinfo\biodemos;H:\MATLAB7\toolbox\bioin fo\biodemos\html;H:\MATLAB7\toolbox\bioinfo\biodemos\ja;H:\MATLAB7\toolbox\bioinfo\bioinfo;H:\MATLAB 7\toolbox\bioinfo\bioinfo\ja; …//结果生成一个字符串 (2)设置 MATLAB 7.0 的搜索路径 方法一:在 MATLAB 7.0 命令窗口中输入 editpath 或 pathtool 命令或通过“File→Set Path” 菜单,进入如图 1-31 所示的“设置搜索路径”对话框,然后通过该对话框可以编辑搜索路径。 方法二:在命令窗口执行“path(path, ’e:\MATLAB ’)”,然后通过图 1-31 所示的“设置搜 索路径”对话框查看“e:\MATLAB”是否在搜索路径中。 方法三:在命令窗口执行“addpath e:\MATLAB -end”,将新的目录加到整个搜索路 径的末尾。如果将 end 改为 begin,可以将新的目录加到整个搜索路径的开始。 –18– 1.6 使用 MATLAB 7.0 帮助系统 第 1 章 MATLAB 概述 MATLAB 7.0 为用户提供了非常完善的帮助系统,例如 MATLAB 7.0 的在线帮助、帮助 窗口、帮助提示、HTML 格式的帮助、pdf 格式的帮助文件以及 MATLAB 7.0 的示例和演示 等。通过使用 MATLAB 7.0 的帮助菜单或在命令窗口中输入帮助命令,可以很容易地获得 MATLAB 7.0 的帮助信息,并能通过帮助进一步学习 MATLAB 7.0。下面分别介绍 MATLAB 7.0 中三种类型的帮助系统。 1.命令窗口查询帮助系统 常见的帮助命令如表 1-8 所示。 表 1-8 常用 MATLAB 7.0 帮助命令 帮助命令 功能 帮助命令 功能 help 获取在线帮助 which 显示指定函数或文件的路径 demo 运行 MATLAB 7.0 演示程序 lookfor 按照指定的关键字查找所有相关的 M 文件 tour 运行 MATLAB 7.0 漫游程序 exist 检查指定变量或文件的存在性 who 列出当前工作空间中的变量 helpwin 运行帮助窗口 whos 列出当前工作空间中变量的更多信息 helpdesk 运行 HTML 格式帮助面板 Help Desk what 列出当前目录或指定目录下的 M-文 doc 件、MAT 文件和 MEX 文件 在网络浏览器中显示指定内容的 HTML 格式帮助文件,或启动 helpdesk 【Help 命令】:在命令窗口用于显示 MATLAB 7.0 函数的帮助。调用格式如下: Help //在命令窗口列出所有主要的基本帮助主题 help / //列出所有运算符和特殊字符 help functionname //在命令窗口列出 functionname M-文件的描述及语法 help toolboxname //在命令窗口列出 toolboxname 文件夹中的内容 help toolboxname/functionname help classname.methodname //显示某一类的函数帮助 help classname help syntax t = help(’topic’) 【示例 1】 >> help HELP topics MATLAB\general MATLAB\ops MATLAB\lang … kernel\embedded - General purpose commands. - Operators and special characters. - Programming language constructs. - xPC Target Embedded Option –19– MATLAB 7.0 从入门到精通 MATLAB7\work - (No table of contents file) e:\MATLAB - (No table of contents file) 【示例 2】 >> help add --- help for hgbin/add.m --- HGBIN/ADD Add method for hgbin object This file is an internal helper function for plot annotation. There is more than one add available. See also help ccshelp/add.m help iviconfigurationstore/add.m help cgrules/add.m help des_constraints/add.m help xregcardlayout/add.m help xregcontainer/add.m help xregmulti/add.m help cgddnode/add.m Reference page in Help browser doc add 【lookfor 命令】:按照指定的关键字查找所有相关的 M 文件。其调用格式如下: lookfor topic lookfor topic –all 【示例】 >> lookfor inverse INVHILB Inverse Hilbert matrix. IPERMUTE Inverse permute array dimensions. ACOS Inverse cosine. ACOSD Inverse cosine, result in degrees. … ADDINVG Add the inverse Gaussian distribution. STDRINV Compute inverse c.d.f. for Studentized Range statistic 2.联机帮助系统 MATLAB 7.0 的联机帮助系统非常全面。用户可以通过下面介绍的方法进入 MATLAB 7.0 的联机帮助系统(如图 1-32 所示)。 • 直接单击 MATLAB 7.0 主窗口中的“ ”按钮; • 选中 Help 菜单的前 4 项中的任意一项; • 在命令窗口中执行 helpwin、helpdesk 或 doc。 下面介绍联机帮助系统的使用方法和技巧。 联机帮助系统界面的菜单项与大多数 Windows 程序界面的菜单含义和用法都差不多,熟 –20– 第 1 章 MATLAB 概述 悉 Windows 的用户可以很容易地掌握,在此不作详细介绍。帮助向导页面包含 4 个页面,分 别是帮助主题(Contents)、帮助索引(Index)、查询帮助(Search)以及演示帮助(Demos)。 如果知道需要查询的内容的关键字,一般可选择 Index 或 Search 模式来查询;只知道需要查 询的内容所属的主题或是只是想进一步了解和学习某一主题,一般可选择 Contents 或 Demos 模式来查询。 图 1-32 联机帮助系统 3.联机演示系统 通过联机演示系统,用户可以直观、快速地学习 MATLAB 7.0 中某个工具箱的使用方法, 它是有关的参考书籍不能替代的。下面就向读者介绍如何使用演示系统。 可以通过以下方式打开联机演示系统。 • 选择 MATLAB 7.0 主窗口菜单的“Help→Demos”选项; • 在命令窗口输入 demos; • 直接在帮助页面上选择 Demos 页。 【示例】 • 在 MATLAB 7.0 命令窗口中执行“>>demos”命令,弹出如图 1-33 所示的界面。 图 1-33 联机演示系统 –21– MATLAB 7.0 从入门到精通 • 在 Demos 页面中选择“Signal Processing”工具箱中的“Spectral Analysis and Statistical Signal Processing”选项(如图 1-34 所示)。 图 1-34 演示示例 • 然后在右边窗口中选择“Power Spectral Density Demo”选项打开对话框。用鼠标单击 页面底部有下划线的文字“Run this demo”,打开如图 1-35 所示的示例界面窗口。在 该窗口中,用户可以选择不同的信号,窗口中就会自动显示该信号的功率谱密度。 图 1-35 “功率谱密度的演示示例界面”窗口 联机演示系统对于学习工具箱以及 MATLAB 7.0 各个方面的应用的用户非常有意义。通 过演示示例,用户可以快速直观地掌握某一工具的使用方法,而不必从枯燥的理论开始学起。 1.7 初识 MATLAB 下面以一个简单的示例向读者展示如何使用 MATLAB 7.0 进行简单的数值计算。 • 用鼠标左键双击桌面上的 MATLAB 7.0 图标 ,进入 MATLAB 7.0 的工作环境界面(如 图 1-14 所示); • 在命令窗口中输入“>>w=2*pi;”,按 Enter 键,可以在工作空间窗口看到变量 w,大 –22– 第 1 章 MATLAB 概述 小为 6.2832; • 在命令窗口中输入“>>y= sin(w*2/3);”,按 Enter 键,可以在工作空间窗口看到变量 y, 大小为-0.86603; • 在命令窗口中输入“>>z=sin(w/3)”,按 Enter 键,可以在工作空间窗口看到变量 z,大 小为 0.86603,命令窗口中显示代码如下: z= 0.8660 • 在命令窗口中输入如下代码: >> w2=pi m=sin(w2/2) • 按 Enter 键,可以在工作空间窗口看到变量 w2 和 m,大小分别为 3.1416 和 1,命令窗 口中显示代码如下: w2 = 3.1416 m= 1 注意:当命令后面有分号时,按 Enter 键后,命令窗口中不显示运算结果;如果无分号,则在命 令窗口中显示运算结果。当希望先输入多条语句,然后再同时执行它们,则在输入下一条 命令时,要按住 Ctrl 键的同时按下 Enter 键进行换行输入。 –23– 第 2 章 MATLAB 基础知识 本章介绍了 MATLAB 7.0 的一些基础知识,包括 MATLAB 的数据类型、基本矩阵操作、 运算符和字符串处理函数。本章的知识是 MATLAB 7.0 编程的基础,熟练掌握了本章知识有 利于学习后面的章节。 2.1 数据类型 MATLAB 7.0 中定义了很多种数据类型,包括整数、浮点数、字符、字符串和逻辑类型 等。用户甚至可以定义自己的数据类型。本小节讨论各种数据类型以及在 MATLAB 7.0 中使 用它们的方法。 在 MATLAB 7.0 中有 15 种基本数据类型。每种基本的数据类型均以矩阵的形式出现, 该矩阵可以是最小的 0×0 矩阵到任意大小的 n 维矩阵。数据类型结构如图 2-1 所示,其中带 下划线的为基本数据类型。下面将依次介绍各种数据类型的用法。 数组 字符类型 数值类型 单元类型 结构类型 Java类 函数句柄 有符号、无符号8位整数 有符号、无符号16位整数 有符号、无符号32位整数 有符号、无符号64位整数 单精度类型 双精度类型 用户类 图 2-1 数据类型结构图 2.1.1 数值类型 数值类型包含整数、浮点数和复数 3 种类型。另外 MATLAB 7.0 还定义了 inf 和 NaN 两 个特殊数值。 1.整数类型 在 MATLAB 7.0 中整数类型包含 4 种有符号整数和 4 种无符号整数。有符号整数可以用 来表示负数、零和正整数,而无符号整数则只可以用来表示零和正整数。MATLAB 7.0 支持 1、2、4 和 8 字节的有符号整数和无符号整数。这 8 种数据类型的名称、表示方法和类型转 第 2 章 MATLAB 基础知识 换函数如表 2-1 所示。应用时要尽可能用字节数少的数据类型表示数据,这样可以节约存储 空间和提高运算速度。例如最大值为 100 的数据可以用 1 个字节的整数来表示,而没有必要 用 8 个字节的整数来表示。 表 2-1 整数的数据类型和表示范围 数据类型名称 数据类型表示范围 类型转换函数 有符号 1 字节整数 有符号 2 字节整数 有符号 4 字节整数 有符号 8 字节整数 无符号 1 字节整数 无符号 2 字节整数 无符号 4 字节整数 无符号 8 字节整数 -27 - 27-1 -215 - 215-1 -231 - 231-1 -263 - 263-1 0 - 28-1 0 - 216-1 0 - 232-1 0 - 264-1 int8() int16() int32() int64() uint8() uint16() uint32() uint64() 在表 1-2 中的类型转换函数可以用于把其他数据类型的数值强制转换为整数类型。此外 类型转换函数还可以用于生成整数类型的数值。例如,如果需要产生一个有符号 2 字节整数 的数值可以用如下语句实现,具体代码如下: x = int16(12531); 2.浮点数类型 MATLAB 7.0 有双精度浮点数和单精度浮点数两种浮点数。双精度浮点数为 MATLAB 7.0 默认的数据类型。如果某个数据没有被显式地指定数据类型,那么 MATLAB 7.0 会用双精度 浮点数来存储它。为了得到其他类型的数值类型,可以使用类型转换函数。 MATLAB 7.0 中的双精度浮点数和单精度浮点数均采用 IEEE 754 中规定的格式来定义。 其表示范围、存储大小和类型转换函数如表 2-2 所示。 表 2-2 数据类型名称 双精度浮点数 单精度浮点数 浮点数的数据类型和表示范围 存储大小 4 字节 8 字节 表示范围 -1.79769*10308~+1.79769*10308 -3.40282*1038~+3.40282*1038 类型转换函数 double() single() 3.复数类型 复数包含独立的两部分,即实部和虚部。虚部的单位是-1 的开平方根,在 MATLAB 7.0 中可以用 i 或者 j 来表示。 可以用如下赋值语句来产生复数: a=5+10i; 也可以用函数 complex 来产生复数,示例代码如下: x=5; y=10; z=complex(x,y); 其中 x、y 为实数,得到的 z 是以 x 为实部,y 为虚部的复数。 也可以这样使用 complex 函数,具体代码如下: x=5; –25– MATLAB 7.0 从入门到精通 z=complex(x); 其中 x 为实数,得到的 z 是以 x 为实部,以 0 为虚部的复数。 4.inf 和 NaN MATLAB 7.0 中规定用 inf、-inf 来表示正无穷大和负无穷大。除法运算中除数为 0 或者 运算结果溢出都会导致 inf 或-inf 的结果。以下 3 条语句运算产生的结果均为 inf,具体代码 如下: 2/0 x = exp(3000) x = log(0) MATLAB 7.0 中规定用 NaN 来表示一个既不是实数也不是复数的数值。NaN 是 Not a Number 的缩写。类似 0/0、inf/inf 这样的表达式得到的结果均为 NaN。 2.1.2 逻辑类型 逻辑类型用 1 和 0 来表示 true 和 false 两种状态。可以用函数 logical()来得到逻辑类型的 数值。函数 logical()可以把任何非零的数值转换为逻辑 true(即 1),把数值 0 转换为逻辑 false (即 0)。示例代码如下: logical(-1) 上述语句得到代码如下: Warning: Values other than 0 or 1 converted to logical 1 ans = 1 在 2.3.2 小节中将介绍的逻辑关系运算符也可以得到逻辑类型的数据。 2.1.3 字符和字符串 MATLAB 7.0 中规定用数据类型 char 来表示一个字符。一个 char 类型的 1×n 数组则可以 称为字符串 string。MATLAB 7.0 中 char 类型都是以 2 字节的 unicode 字符来存储的。 可以用一对单引号来表示字符串,例如下面的代码: str='I am a great person'; 也可以用 char 函数来构造一个字符串,例如下面的代码: str=char('[65 66]'); 上述语句得到字符串'AB'。 关于字符串的更深入的介绍请参考本书 2.4 小节。 2.1.4 函数句柄 函数句柄是 MATLAB 7.0 中用来提供间接调用函数的数据类型。函数句柄可以转递给其 他函数以便该函数句柄所代表的函数可以被调用。函数句柄还可以被存储起来,以便以后利 用。 –26– 第 2 章 MATLAB 基础知识 函数句柄可以用符号@后面跟着函数名来表示,例如下面的代码: fhandle=@sin; sin 为 MATLAB 7.0 中自带的正弦函数,得到的输出变量 fhandle 为 sin 函数的句柄。可 以利用 fhandle 来调用 sin 函数,例如下面的代码: fhandle(0) 上面语句得到的输出代码如下: ans = 0 实际上,该程序中的语句 fhandle(0)相当于语句 sin(0)。 2.1.5 结构体类型 结构体是根据属性名组织起来的不同类型数据的集合。有一种容易与结构体类型混淆的 数据类型是单元数组类型,它是一种特殊类型的 MATLAB 7.0 数组,它的每一个元素叫做单 元,而每一个单元包含 MATLAB 7.0 数组。结构体和单元数组的共同之处在于它们都提供了 一种分级存储机制来存储不同类型的数据,不同之处是组织数据的方式不一样。结构体数组 里的数据是通过属性名来引用的,而在单元数组里,数据是通过单元数组下标引用来操作的。 本节将介绍结构体数组,而单元数组将在 2.1.6 小节中介绍。 结构体数组是一种由“数据容器”组成的 MATLAB 7.0 数组,这种“数据容器”称为结 构体的属性(field)。结构体的任何一个属性可以包含任何一种类型的数据。如图 2-2 所示是 一个结构体,它有所 3 个属性,即 Name、Score 和 Salary,其中 Name 是一个字符串,Score 是一个标量,Salary 是一个 1×5 的向量。 Personel Name Score Salary ‘Clayton’ 98.5 4500 5100 5600 5200 4800 图 2-2 结构体的属性 和其他的数据类型一样,结构体也是一种数组。一个单独的结构体就是一个 1×1 的结构 体数组。用户可以构造任意维数和形状的结构体数组,当然也包含多维结构体数组。 1.结构体数组的构造 构造一个结构体数组有如下两种方法: • 利用赋值语句; • 利用函数 struct()来进行定义。 下面就通过为结构体中的每一个属性赋值来构造一个结构体数组。例如,建立如图 2-2 所示的结构体数组 Personel,可以用如下语句: Personel.Name='Clayton'; Personel.Score=98.5; –27– MATLAB 7.0 从入门到精通 Personel.Salary=[4500 5100 5600 5200 4800]; Personel 上述语句得到输出代码如下: %在命令行输出 Personel 变量的信息 Personel = Name: 'Clayton' Score: 98.5000 Salary: [4500 5100 5600 5200 4800] 还可以用如下语句把结构体数组扩展成 1×2 的结构体,代码设置如下: Personel(2).Name='Dana'; Personel(2).Score=100; Personel(2).Salary=[6700 9000]; 由上述语句使结构体数组 Personel 的维数变为 1×2。当用户扩展结构体数组时,MATLAB 7.0 对未指定数据的属性自动赋值成空矩阵,使其满足以下规则: • 数组中的每个结构体都具有同样多的属性名; • 数组中的每个结构体都具有相同的属性名。 例如,下面语句使结构体数组 Personel 的维数变为 1×3,此时 Personel(3).Score 和 Personel(3). Salary 由于未指定数据,MATLAB7.0 将其设为空矩阵。 Personel(3).Name='John'; 要注意的是结构体数组中元素属性的大小并不要求一致,例如结构数组 Personel 中的 Name 属性和 Salary 属性都具有不同的长度。 除了使用赋值语句来构造结构数组外,还可以用函数 struct()来实现构造结构数组。函数 struct 的基本调用格式为: strArray = struct('field1',val1,'field2',val2, ...) 上面语句中的输入变量为属性名和相应的属性值。 函数 struct()可以有不同的调用方法来实现构造结构体矩阵,例如要实现一个 1×3 的结构 数组 Personel 的方法如表 2-3 所示。 表 2-3 方法 使用 struct 函数的方法 调用格式 初始值状况 单独使用 struct 函数 Personel(3)=struct('Name','John','Score',85.5,'Salary',[4500 4200]) Personel(1) 和 Personel(2) 的 属 性值都是空矩 阵 , Personel(3) 的值如输入 struct 函 数 与 repmat 函数配合 使用 repmat(struct('Name','John','Score',85.5,'Salary',[4500 4200]),1,3) 数组的所有元素 具有和输入一样 的值 struct 函 数 结构数组的属性 的 输 入 为 struct('Name',{'Clayton','Dana','John'},'Score',{98.5,100,85.5},'Salary',{[4500 值由单元数组指 单元数组 4200],[],[]}) 定 –28– 2.访问结构体数组的数据 第 2 章 MATLAB 基础知识 使用结构体数组的下标引用,可以访问结构体数组任何元素及其属性。同样也可以给任 何元素及其属性赋值。例如有一个结构体数组如图 2-3 所示,它可以通过下面语句来生成: Personel=struct('Name',{'Clayton','Dana','John'},'Score',{98.5,100,[]},'Salary',{[4500 5100 5600 5200 4800],[6700 9000],[]}) 用户可以访问结构数组的任意子数组。例如,下面的命令行生成一个 1×2 的结构数组: NewPersonel= Personel(1:2) 上述语句得到输出代码如下: NewPersonel = 1x2 struct array with fields: Name Score Salary 98.5 图 2-3 结构数组 如果要访问结构体数组的某个元素的某个属性,可以用如下格式: Personel(2).Name 上述语句得到输出代码如下: ans = Dana 如果要访问结构体数组的某个元素的某个属性的元素值,可以使用如下格式: Personel(1).Salary(3) 上述语句得到的输出代码如下: ans = 5600 如果想得到结构体数组的所有元素的某个属性值,可以使用如下格式: Personel.Name 上述语句得到输出代码如下: ans = Clayton ans = Dana –29– MATLAB 7.0 从入门到精通 ans = John 以上结果表明 Personel.Name 格式的输入将返回结构体数组的所有元素的属性值。可以 使用矩阵合并符[]来合并这些结果,程序语句如下: Salary=[Personel.Salary] 上述语句得到输出代码如下: Salary = 4500 5100 5600 5200 4800 6700 9000 也可以用把它们合并在一个单元数组里,代码设置如下: Salary={Personel.Salary} 上述语句得到输出代码如下: Salary = [1x5 double] [1x2 double] [] 2.1.6 单元数组类型 单元数组就是每个元素为一个单元的数组。每个单元都可以包含任意数据类型的 MATLAB 7.0 数组。例如,单元数组的一个单元可以是一个实数矩阵,或是一个字符串数组, 也可以是一个复向量数组。图 2-4 所示是一个 2×3 的单元数组,可见其每一个单元内的数据 类型均不相同。 图 2-4 单元数组示意图 1.单元数组的构造 构造单元数组有左标志法和右标志法两种方法。下面就详细介绍这两种方法。 –30– 第 2 章 MATLAB 基础知识 • 左标志法 左标志法就是把单元标志{}放在左边,例如,创建一个 2×2 的单元数组可以使用如下语 句: c{1,1}='Clayton'; c{1,2}=eye(3,3); c{2,1}=@sin; c{2,2}=true; • 右标志法 右标志法就是把标志符放在右边,例如,创建和上面一样的单元数组可以使用如下语句: c(1,1)={'Clayton'}; c(1,2)={eye(3,3)}; c(2,1)={@sin}; c(2,2)={true}; 上述语句还可以简单地写为下面的代码: c={'clayton',eye(3,3);@sin,true} 要显示单元数组可以在命令行中直接输入单元数组的名字,代码设置如下: c 由上述语句得到输出代码如下: c= 'Clayton' [3x3 double] [sin ] [ 1] 另一种显示单元数组的方法是使用函数 celldisp(),代码如下: celldisp(c) 由上述语句得到的输出代码如下: c{1,1} = clayton c{2,1} = @sin c{1,2} = 100 010 001 c{2,2} = 1 值得注意的是函数 celldisp()的显示格式与直接输入单元数组名的显示格式是不同的。 celldisp()函数更适用于具有大量数据的单元数组的显示。 2.单元数组的读取 以上小节中的单元数组 c 作为例子。要读取 c{1,1}中的字符串,可以使用如下语句: Str= c{1,1} –31– MATLAB 7.0 从入门到精通 由上述语句得到输出代码如下: Str = clayton 如果要读取单元数组的若干个单元的数据,例如读取单元数组 c 的第二行,可以用下面 语句: c(2,:) 上述语句得到输出代码如下: ans = [sin] [1] 3.单元数组的删除 将空矩阵赋给单元数组的某一整行或者某一整列,就可以删除单元数组的这一行或者一 列。 例如,删除单元数组 c 的第一行可以用如下语句: c(1,:)=[] 上述语句得到输出代码如下: c= [sin] [1] 2.2 基本矩阵操作 MATLAB 7.0 是基于矩阵运算的一个软件,所有的数据均以二维矩阵或高维矩阵的形式 存储,因此 MATLAB 7.0 又被称为矩阵实验室。 MATLAB 7.0 中最基本的数据结构是二维的矩阵。二维的矩阵可以方便地存储和访问大 量数据。每个矩阵的单元可以是数值类型、逻辑类型、字符类型或者其他任何的 MATLAB 7.0 数据类型。无论是单个数据还是一组数据,MATLAB 7.0 均采用二维的矩阵来存储。对于一 个数据,MATLAB 7.0 用 1×1 矩阵来表示;对于一组数据,MATLAB 7.0 用 1×n 矩阵来表示, 其中 n 是这组数据的长度。MATLAB 7.0 也支持多维的矩阵,MATLAB 7.0 中称这类数据为 多维数组(array)。 为了方便,把 1×1 的矩阵称为标量,把 1×n 的矩阵称为向量。把至少有一维的长度为 0 的矩阵称为空矩阵,空矩阵可以用[]来表示。 例如,实数 1.5 是 1×1 的双精度浮点数类型矩阵。在 MATLAB 7.0 中可以用语句 whos 来显示数值的数据类型和储存矩阵大小。以下语句可以用于查看实数 1.5 的数据类型和储存 矩阵大小: a=1.5; whos a %显示变量 a 的信息 由上述语句得到输出代码如下: Name Size Bytes Class a 1x1 8 double array –32– 第 2 章 MATLAB 基础知识 例如,字符串 'I am a great person'是 1×19 的字符矩阵类型矩阵,可以用如下语句来查看 该字符串的数据类型和储存矩阵大小: str='I am a great person'; whos str 由上述语句得到输出代码如下: Name Size Bytes Class str 1x19 38 char array 2.2.1 矩阵的构造 1.简单矩阵构造 最简单的构造矩阵方法是采用矩阵构造符[],构造一行的矩阵可以把矩阵元素放在矩阵 构造符[]中,并以空格或者逗号来隔开它们,其代码设置如下: row = [E1, E2, ..., Em] 或者 row = [E1 E2 ... Em] 例如,一个 1×4 的矩阵可以通过如下语句得到: a=[1 2 3 4] 或者是下面的代码: a=[1,2,3,4] 如果矩阵是多行的,行与行之间用分号隔开,其代码设置如下: A = [row1; row2; ...; rown] 例如一个 3×4 的矩阵可以用如下语句得到: A=[1,2,3,4;5,6,7,8;9,10,11,12] 上述语句得到矩阵 A 如下: A= 1234 5678 9 10 11 12 2.特殊矩阵构造 MATLAB 7.0 还提供一些函数用来构造一些特殊的矩阵,这些函数如表 2-4 所示。 表 2-4 函数名 ones zeros eye 函数用途 产生矩阵元素全为 1 的矩阵 产生矩阵元素全为 0 的矩阵 产生单位矩阵,即主对角线 上的元素为 1,其他元素全为 0 特殊矩阵函数 基本调用格式 A=ones(n) 产生 n×n 的 1 A=ones(m,n) 产生 m×n 的 1 A=zeros(n) 产生 n×n 的 0 A=zeros(m,n) 产生 m×n 的 0 A=eye(n) 产生 n×n 的单位矩阵 A=eye (m,n) 产生 m×n 的单位矩阵 –33– MATLAB 7.0 从入门到精通 函数名 Diag magic rand randn randperm compan 函数用途 把向量转化为对角矩阵或者 得到矩阵的对角元素 产生魔方矩阵,即每行、每 列之和相等的矩阵 产生 0~1 均匀分布的随机数 产生均值为 0,方差为 1 高斯 分布的随机数 产生整数 1~n 的随机排列 产生多项式的伴随矩阵 X=diag(v,k) X=diag(v) v=diag(X,k) v=diag(X) 续表 基本调用格式 把向量 v 转换为一个对角矩阵。 把向量 v 转换为一个主对角矩阵。 得到矩阵 X 的对角元素 得到矩阵 X 的主对角元素 magic(n) 产生 n×n 的魔方矩阵 Y = rand(n) 产生 n×n 的 0-1 均匀分布的随机数 Y = rand(m,n) 产生 m×n 的 0-1 均匀分布的随机数 Y = randn(n) 产生 n×n 的标准高斯分布的随机数 Y = randn(m,n) 产生 m×n 的标准高斯分布的随机数 p = randperm(n) 产生整数 1 到 n 的随机排列 A = compan(u) 产生多项式 u 的伴随矩阵 例如要产生一个 3×4 的全 0 矩阵,可以采用函数 zeros()实现,代码设置如下: a=zeros(3,4) 由上述语句得到矩阵: a= 0000 0000 0000 例如要产生一个 1×5 的 0~1 均匀分布的随机数,实现方法如下: a=rand(1,10) 由上述语句得到矩阵: a= 0.4186 0.8462 0.5252 0.2026 0.6721 值得注意的是函数 rand()是以机器时间作为随机种子的,每次运行上述语句得到的结果 都是不同的。 3.向量的构造 向量在构造矩阵和对矩阵进行索引时是很有用的,因此 MATLAB 7.0 提供了专门用于产 生向量的运算符—“:”。 冒号可以用来构造步长为 1 的递增向量,其格式为: a:b 上式产生从 a~b 的以步长为 1 递增向量。例如: A = 1:5 上述语句生成的结果是: A= 1 2 3 4 5 向量中的元素不必是正整数,可以是任何整数。例如下面的代码: A=-3.2:2.2 由上述语句生成的结果是: A = -3.2000 -2.2000 -1.2000 -0.2000 –34– 0.8000 1.8000 第 2 章 MATLAB 基础知识 在默认的情况下,MATLAB 7.0 总是保证步长精确地为 1,即使在 b 值不是距离 a 值整 数距离时也如此。例如下面的代码: A=1:5.2 由上述语句生成的结果是: A= 1 2 3 4 5 如果 b 值小于 a 值,则 MATLAB 7.0 返回一个空矩阵。例如下面的代码: A=3:1 由上述语句生成的结果是: A = Empty matrix: 1-by-0 MATLAB 7.0 也支持产生任意步长的向量,步长甚至可以是负数。例如下面的代码: y=0:pi/4:pi 上面语句产生从 0 开始到 pi 结束的以 1/4*pi 为步长的向量。由上述语句生成的结果是: y= 例如: 0 0.7854 1.5708 2.3562 3.1416 z=5:-1:1 上面语句产生从 5 开始到 0 结束的以-1 为步长的向量。上述语句生成的结果是: z= 5 4 3 2 1 能够构造向量的其他函数还有 linspace()函数和 logspace()函数。Linspace()函数用于创建 指定长度的等距向量。例如下面的代码: A=linspace(-6,6,4) 上述语句产生-6,6 之间等间距的 4 个点。由上述代码生成的结果是: A = -6 -2 2 6 logspace()函数用于创建对数等距的向量,其使用方法与 linspace()函数完全相同。 2.2.2 矩阵大小的改变 1.矩阵的合并 矩阵的合并就是把两个或者两个以上的矩阵数据连接起来得到一个新的矩阵。前面介绍 的矩阵构造符[]不仅可用于构造矩阵,同时还可以作为一个矩阵合并操作符。表达式 C=[A B] 在水平方向合并矩阵 A 和 B,而表达式 C=[A;B]在竖直方向合并矩阵 A 和 B。 例如: a=ones(2,3); b=zeros(2,3); c=[a;b] 由上述语句得到结果代码如下: c= 111 111 000 –35– MATLAB 7.0 从入门到精通 000 例如下面的代码: a=ones(2,3); b=zeros(2,3); c=[a b] 由上述语句得到结果代码如下: c= 111000 111000 可以用矩阵合并符来构造任意大小的矩阵。不过需要注意的是在矩阵合并的过程中一定 要保持矩阵的形状是方形,否则矩阵合并将无法进行。图 2-5 表明具有相同高度的两个矩阵 可以在水平方向合并为一个新的矩阵。而图 2-6 则表明不具有相同高度的两个矩阵,不允许 合并为一个矩阵。 图 2-5 正确的矩阵合并 12 456 12456 + ≠ 3 4 789 34789 56 56 3×2 2×3 图 2-6 不正确的矩阵合并 除了使用矩阵合并符[]来合并矩阵外,还可以使用矩阵合并函数来合并函数。这些矩阵 合并函数的函数描述和基本调用格式如表 2-5 所示。 表 2-5 矩阵合并函数 函数名 函数描述 基本调用格式 cat 在指定的方向合并矩阵 cat(DIM,A,B) cat(2,A,B) cat(1,A,B) 在 DIM 维方向合并矩阵 A,B 与[A B]用途一致 与[A;B] 用途一致 horzcat 在水平方向合并矩阵 horzcat(A,B) 与[A B]用途一致 vertcat 在竖直方向合并矩阵 vertcat(A,B) 与[A;B]用途一致 repmat blkdiag 通过复制矩阵来构造新的矩阵 用已知矩阵来构造块对角化矩阵 B = repmat(A,M,N) 得到 M×N 个 A 的大矩阵 Y = blkdiag(A,B,...) 得到以矩阵 A、B…等为对角块的矩 阵Y 例如下面的代码: a=[1 2;3 4]; b=repmat(a,2,3) 由上述语句得到输出代码如下: b= 121212 –36– 343434 121212 343434 又例如下面的代码: a=eye(2)*3; b=magic(3); c=blkdiag(a,b) 由上述语句得到输出代码如下: c= 30000 03000 00816 00357 00492 第 2 章 MATLAB 基础知识 2.矩阵行列的删除 要删除矩阵的某一行或者是某一列,只要把该行或者该列赋予一个空矩阵[]即可。例如 有一个 4×4 的魔方矩阵,代码设置如下: A = magic(4) 上述语句得到矩阵 A: A= 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 如果想删除矩阵的第 2 行,则可以用如下语句: A(2,:)=[] 由上述语句得到新的矩阵 A 如下: A= 16 2 3 13 9 7 6 12 4 14 15 1 2.2.3 矩阵下标引用 本小节将介绍用矩阵下标来读取和改写矩阵元素的值的方法。包括如何用矩阵下标访问 单个矩阵阵元素、如何线性引用矩阵元素、引用矩阵元素方式转换和访问多个矩阵元素。 1.矩阵下标访问单个矩阵阵元素 若 A 是一个 2 维矩阵,可以用 A(i, j)来表示第 i 行第 j 列的元素。例如,假设 A=magic(3), –37– MATLAB 7.0 从入门到精通 那么 A(3,2)代表第 3 行第 2 列的数字。代码设置如下: A=magic(3) b=A(3,2) 由上述语句得到输出代码如下: A= 816 357 492 b= 9 也可以通过下标来改变矩阵的值,代码设置如下: A(3,2)=0 由上述语句得到新的矩阵 A 如下: A= 816 357 402 如果要访问多维矩阵可以通过指定多个下标来实现。 2.线性引用矩阵元素 在 MATLAB 7.0 中可以通过单下标来引用矩阵元素,其引用格式为 A(k)。通常这样的引 用是用于行向量或列向量。但是这样的引用也可以用于二维矩阵。MATLAB 7.0 存储矩阵元 素时并不是按照其命令行输出矩阵的格式来存储矩阵的。实际上,矩阵可以看成是按列优先 排列的一个长列向量格式来存储的。例如下面的代码: A = [2 6 9; 4 2 8; 3 0 1] 由上述语句得到矩阵: A= 269 428 351 矩阵 A 实际上在内存中是被存储成以 2、4、 3、 6、 2、 5、 9、 8、 1 排列的一个 列向量。A 矩阵的第 3 行第 2 列,也就是值为 5 的元素实际上在存储空间是第 6 个元素。 要访问这个元素,你可以用 A(3,2)格式,也可以用 A(6)格式。后者也就是线性引用矩阵元 素方法。 一般地,设矩阵 A 是一个 M×N 的矩阵,矩阵元素 A(i,j)如果换成用线性引用矩阵元素方 法来表示可以表示为 A((j-1)*M+i)。在上面的例子中 A(3,2)=A((2-1)*3+3)=A(6)。 3.引用矩阵元素方式转换 如果你有矩阵的下标,但是你却想用线性引用矩阵元素方法来访问矩阵,你可以用 sub2ind()函数来得到线性引用的下标。例如下面的代码: –38– 第 2 章 MATLAB 基础知识 A = [2 6 9; 4 2 8; 3 0 1]; linearindex = sub2ind(size(A), 3, 2) 由上述语句得到输出代码如下: linearindex = 6 反之,如果想从线性引用的下标得到矩阵的下标可以用函数 ind2sub(),例如下面的代码: [row col] = ind2sub(size(A), 6) 由上述语句得到输出代码如下: row = 3 col = 2 4.访问多个矩阵元素 设 A=magic(4),如果需要计算第 4 列元素的和,按照前面介绍的方法可以用下式来实现: A(1,4) + A(2,4) + A(3,4) + A(4,4) 在下标表达式里,可以用冒号来表示矩阵的多个矩阵元素。例如,A(1:k,j)表示矩阵第 j 列的前 k 个元素。利用冒号,第 4 列元素的和可以用更为简洁的式子,代码设置如下: sum(A(1:4, 4)) 还有更简洁的方法,因为冒号本身可以表示一列或者一行的所有元素,所以上式可以表 达为: sum(A(:,4)) 在 MATLAB 7.0 中提供了一个关键字 end,用于表示该维中的最后一个元素。所以上式 还可以改写成: sum(A(:,end)) 实际上还可以用冒号来表示非相邻的多个元素,例如下面的代码: A=1:10 B=A(1:3:10) 由上述语句得到输出代码如下: A= 1 2 3 4 5 6 7 8 9 10 B= 1 4 7 10 2.2.4 矩阵信息的获取 本小节介绍如何获取矩阵的信息,包括矩阵的尺寸大小,矩阵元素的数据类型和矩阵的 数据结构。 1.矩阵尺寸信息 矩阵尺寸函数可以得到矩阵的形状和大小信息,这些函数如表 2-6 所示。 –39– MATLAB 7.0 从入门到精通 表 2-6 矩阵尺寸函数 函数名 函数描述 基本调用格式 length 矩阵最长方向的长度 n = length(X) 相当于 max(size(X)) ndims numel size 矩阵的维数 矩阵的元素个数 矩阵在各个方向的长度 n = ndims(A) n = numel(A) d = size(X) [m,n] = size(X) m = size(X,dim) 矩阵的维数 矩阵的元素个数 返回的大小信息以向量方式存储 返回的大小信息以分开存储 返回某一位的大小信息 下面例子用于表明如何使用矩阵尺寸信息获取函数。 设矩阵 A 为: A=rand(3,5)*10 上述语句得到矩阵 A 如下: A= 4.0571 4.1027 3.5287 1.3889 6.0379 9.3547 8.9365 8.1317 2.0277 2.7219 9.1690 0.5789 0.0986 1.9872 1.9881 求矩阵 A 的元素平均值可以用 numel 函数来实现,代码设置如下: mean=sum(A(:))/numel(A) 由上述语句得到均值如下: mean = 4.2740 在上式中 sum(A(:))表示对 A 的所有元素求和,函数 sum 的用法将在第 3 章中介绍。 2.矩阵元素的数据类型 获得矩阵元素的数据类型信息的函数如表 2-7 所示。 表 2-7 获得矩阵元素的数据类型的函数 函数名 函数描述 基本调用格式 class isa iscell iscellstr 返回输入数据的数据类型 判断输入数据是否为指定数据类型 判断输入数据是否为单元型 判断输入数据是否为单元型的字符 串 C = class(obj) K = isa(obj,'class_name') tf = iscell(A) tf = iscellstr(A) ischar isfloat isinteger islogical isnumeric isreal isstruct 判断输入数据是否为字符数组 判断输入数据是否为浮点数 判断输入数据是否为整数 判断输入数据是否为逻辑型 判断输入数据是否为数值型 判断输入数据是否为实数 判断输入数据是否为结构体 tf = ischar(A) tf =isfloat(A) tf =isinteger(A) tf = islogical(A) tf = isnumeric(A) tf = isreal(A) tf = isstruct(A) 例如矩阵 A 是一个具有实数和复数的矩阵,如果需要将实数和复数分开,可以使用表 2-7 中的函数来实现,其方法如下: %定义一个具有实数和复数的矩阵 A=[5 6.5 2+3i 3.5 6 1+2i]; %定义存储实数和复数的矩阵目前为空矩阵 –40– real_array=[]; complex_array=[]; for i=1:length(A), %判断矩阵元素是否为实数 if isreal(A(i))==1, real_array=[real_array A(i)]; else complex_array=[complex_array A(i)]; end; end; %输出实数元素 real_array %输出复数元素 complex_array 由上述语句得到输出代码如下: real_array = 5.0000 6.5000 3.5000 6.0000 complex_array = 2.0000 + 3.0000i 1.0000 + 2.0000i 第 2 章 MATLAB 基础知识 3.矩阵的数据结构 测试矩阵是否为某一种数据结构的函数如表 2-8 所示。 表 2-8 函数名 isempty isscalar issparse isvector 测试矩阵数据结构的函数 函数描述 测试矩阵是否为空矩阵 测试矩阵是否为标量 测试矩阵是否为稀疏矩阵 测试矩阵是否为矢量 tf = isempty(A) tf = isscalar(A) tf = issparse(A) tf = isvector(A) 基本调用格式 2.2.5 矩阵结构的改变 可以改变矩阵结构的函数如表 2-9 所示。 表 2-9 函数名 reshape rot90 fliplr flipud flipdim transpose 改变矩阵结构的函数 函数描述 按照列的顺序重新排列矩阵元素 旋转矩阵 90° 以竖直方向为轴做镜像 以水平方向为轴做镜像 以指定的轴做镜像 矩阵的转秩 基本调用格式 B = reshape(A,m,n) B = rot90(A) B = rot90(A,k) 把矩阵 A 变为 m×n 大小 旋转矩阵 90° 旋转矩阵 k*90°,k 为整数 B = fliplr(A) B = flipud(A) B = flipdim(A,dim) dim=2 B = transpose(A) dim=1 以水平方向为轴做镜像, 以竖直方向为轴做镜像 相当于 B=A.' –41– MATLAB 7.0 从入门到精通 续表 ctranspose 矩阵的共轭转秩 B = ctranspose(A) 相当于 B=A' 下面以一些例子来说明这些函数的用法。下面例子中的矩阵 A 均如下定义: A = [1 4 7 10; 2 5 8 11; 3 6 9 12] 即矩阵 A 为: A= 1 4 7 10 2 5 8 11 3 6 9 12 例如,当要将一个 3×4 的矩阵按照列的顺序重新排列成 2×6 的矩阵时,可以用下列语句 来实现: B = reshape(A, 2, 6) 由上述语句得到矩阵 B 为: B= 1 3 5 7 9 11 2 4 6 8 10 12 例如,需要把矩阵旋转 90°,可以用下面语句来实现: B = rot90(A) 由上述语句得到矩阵 B 为: B= 10 11 12 789 456 123 例如,需要把矩阵以竖直方向为轴做镜像,可以用下面语句来实现: B = fliplr(A) 由上述语句得到矩阵 B 为: B= 10 7 4 1 11 8 5 2 12 9 6 3 2.2.6 稀疏矩阵 在 MATLAB 7.0 中可以用两种方式来存储矩阵,即满矩阵存储方式和稀疏矩阵存储方式, 简称满矩阵和稀疏矩阵。 在很多情况下一个矩阵只有少数的元素是非零的,而前面的章节中介绍的矩阵对于零值 和非零值是花费同样空间来存储的,因此这种存储方式会浪费很多存储空间,有时还会减慢 计算的速度。这种存储方式称为满矩阵存储方式。而稀疏矩阵在 MATLAB 7.0 内部是以非零 元素和非零元素的行列指标数组来表示的。因此,稀疏矩阵提供了一种针对矩阵元素大多数 –42– 第 2 章 MATLAB 基础知识 都是零值矩阵的有效存储方式。 用户可以创建双精度类型、复数类型和逻辑类型的稀疏矩阵,所有 MATLAB 7.0 自带的 数学函数、逻辑函数和引用操作均可以使用在稀疏矩阵上。稀疏矩阵不能自动生成,定义在 满矩阵的运算只能生成满矩阵,不论多少个元素为零。但是,一旦以稀疏矩阵来存储,那么 稀疏矩阵的存储方式就会传播下去。即定义在稀疏矩阵上的运算生成稀疏矩阵,定义在满矩 阵上的运算生成满矩阵。 1.稀疏矩阵的存储方式 MATLAB 7.0 内部使用 3 个矩阵来存储稀疏矩阵。设有一个 m×n 的矩阵有 nnz 个非零元 素,存储在长度为 nzmax 的矩阵中。 • 第 1 个矩阵存储着所有的非零元素,该矩阵的长度为 nzmax; • 第 2 个矩阵存储着所有的非零元素的行指标,该矩阵的长度也为 nzmax; • 第 3 个矩阵存储着每一列的开始处指针和一个标志着这 3 个矩阵结束的指针,该矩阵 的长度为 n+1。 一个稀疏矩阵要求存储 nzmax 个浮点数和 nzmax+n+1 个整数,因此存储一个稀疏矩阵需 要 8*nzmax + 4*(nzmax+n+1)个字节的单元。 复数类型的稀疏矩阵用第 4 个矩阵来存储非零元素的虚部。一个复数只要其实部和虚部 的其中一个为非零元素,该复数就是非零。 2.稀疏矩阵的创建 MATLAB 7.0 提供转换函数可以从满矩阵得到稀疏矩阵,即 sparse()函数。其调用格式为 S = sparse(A),其中 A 为满矩阵。例如下面的示例代码: A=[0 0 1 0 0200 3000 0 0 4 0]; S = sparse(A) 由上述语句得到输出代码如下: S= (3,1) 3 (2,2) 2 (1,3) 1 (4,3) 4 把一个稀疏矩阵转换为一个满矩阵,可以用转换函数 full()。例如下面的示例代码: A = full(S) 由上面语句返回得到矩阵 A。 还可以用 sparse()函数来直接创建稀疏矩阵,其调用格式为 S = sparse(i,j,s,m,n),其中 i 和 j 分别是稀疏矩阵非零元素的行和列指标,s 为相应的非零元素的值。m 和 n 分别是矩阵的 行高度和列高度。上述稀疏矩阵可以用直接方法创建,语句如下: S=sparse([1,2,3,4],[3,2,1,3],[1,2,3,4],4,4) –43– MATLAB 7.0 从入门到精通 由上述语句得到输出代码如下: S= (3,1) 3 (2,2) 2 (1,3) 1 (4,3) 4 MATLAB 7.0 还提供一些函数用于创建一些特殊的稀疏矩阵,这些函数如表 2-10 所示。 表 2-10 特殊稀疏矩阵创建函数 函数名 函数描述 基本调用格式 speye 创建单位稀疏矩阵 S = speye(m,n) S = speye(n) 创建 m×n 单位稀疏矩阵 创建 n×n 单位稀疏矩阵 spones 创建非零元素为 1 的稀疏 矩阵 R = spones(S) 把矩阵 S 的非零元素的值改为 1 sprand 创建非零元素为均匀分布 的随机数的稀疏矩阵 R = sprand(S) 的随机数 把矩阵 S 的非零元素的值改为均匀分布 R = sprand(m,n,density) 创建非零元素密度为 density 的 m×n 大小的均匀分布的随机数 sprandn 创建非零元素为高斯分布 的随机数的稀疏矩阵 R = sprandn(S) 把矩阵 S 的非零元素的值改为高斯分布的 随机数 R = sprandn(m,n,density) 创建非零元素值密度为 density 的 m×n 大小的高斯分布的随机数 sprandsym 创建非零元素为高斯分布 的随机数的对称稀疏矩阵 R = sprandsym(S) 返回对称随机稀疏矩阵,其下三角和主 对角结构与 S 相同 R = sprandsym(n,density) 返回 n×n 的对称随机稀疏矩阵, 其非零元素密度为 density spdiags 创建对角稀疏矩阵 A = spdiags(B,d,m,n) 把 B 中的值放在 d 中指定的对角线 上,创建一个 m×n 的稀疏矩阵 spalloc 为稀疏矩阵分配空间 S = spalloc(m,n,nzmax) 相当于 sparse([],[],[],m,n,nzmax) 还可以将外部的稀疏矩阵转换成为内部的形式。如果 ASCII 码文件中存有一个稀疏矩阵 的[i,j,v],即非零值的行列指标和非零值,则可以用 load()函数把该文件加载到 MATLAB 7.0 空间,然后用函数 spconvert()把它转换为稀疏矩阵。例如,文件 t.dat 的内容如下: 31 3 22 2 13 1 43 4 然后用如下语句得到稀疏矩阵: load t.dat S = spconvert(t) 上述语句得到输出代码如下: S= –44– (3,1) 3 (2,2) 2 (1,3) 1 (4,3) 4 3.查看稀疏矩阵 第 2 章 MATLAB 基础知识 MATLAB 7.0 还提供一些函数,用于得到稀疏矩阵的定量信息和图形化信息。这些函数 包括得到稀疏矩阵非零值信息和图形化稀疏矩阵。 查看稀疏矩阵非零值信息的函数如表 2-11 所示。 表 2-11 函数名 查看稀疏矩阵非零值信息的函数 函数描述 基本调用格式 nnz nonzeros nzmax 返回非零值的个数 返回非零值 返回用于存储非零值的空间长度 n = nnz(X) s = nonzeros(A) n = nzmax(S) 为了尝试上面函数的用法,可以加载 MATLAB 7.0 自带的稀疏矩阵 west0479,代码设置 如下: load west0479 whos west0479 由上述语句得到输出代码如下: Name Size Bytes Class west0479 479x479 24564 double array (sparse) 为了得到该矩阵非零值的个数,可以用如下语句: nnz(west0479) 由上述语句得到输出代码如下: ans = 1887 为了得到用于存储非零值的空间长度,可以用如下语句: nzmax(west0479) 上述语句得到输出如下: ans = 1887 有时用图形来显示矩阵非零值的分布是很有用的,在 MATLAB 7.0 中 spy()函数实现了这 一功能。为了查看稀疏矩阵 west0479 的非零值的分布,可以用如下语句: spy(west0479) 由上述语句得到如图 2-7 所示的图形。 –45– MATLAB 7.0 从入门到精通 图 2-7 稀疏矩阵图 4.稀疏矩阵的运算规则 在 MATLAB 7.0 系统中的各种命令都可以用于稀疏矩阵的运算。但有稀疏矩阵参加运算 时,所得到的结果将遵循以下的规则: • 把矩阵变为标量或者定长向量的函数总是给出满矩阵; • 把标量或者定长向量变换到矩阵的函数(zeros()、ones()、eye()、rand()等)总是给出满矩 阵;而能给出稀疏矩阵结果的相应的函数有 speye()和 sprand()等; • 从矩阵到矩阵或者向量的变换函数将以原矩阵的形式出现。即定义在稀疏矩阵上的运 算生成稀疏矩阵,定义在满矩阵上的运算生成满矩阵。例如 chol(S)、max(S)和 sum(S) 等函数; • 两个矩阵运算符(如+、-、*、\、|)操作后的结果一般都是满矩阵,除非参加运算的 矩阵都是稀疏矩阵,或者操作本身(如.*,&)保留矩阵的稀疏性; • 参与的矩阵扩展(如[ A B;C D])的子矩阵中,只要有一个是稀疏的,那么所得的结果 也是稀疏的; • 在矩阵引用中,将仍以原矩阵形式给出结果。若 S 矩阵是稀疏的,而 Y 矩阵是全元素 的,不管 I、J 是标量还是向量,那么“右引用”Y=S(I,J)产生稀疏矩阵,而“左引用” S(I,J)=Y 产生满矩阵。 2.3 运算符和特殊符号 MATLAB 7.0 中提供了丰富的运算符,非常方便地满足用户的各种应用。这些运算符包 括算数运算符、关系运算符和逻辑运算符三种运算符。 2.3.1 算数运算符 MATLAB 7.0 的算数运算符的用法和功能如表 2-12 所示。 –46– 第 2 章 MATLAB 基础知识 表 2-12 算数运算符的用法和功能 运算符 用法 功能描述 + A+B 加法或者一元运算符正号。A+B 把矩阵 A 和 B 相加。A 和 B 必须是具有相同长度 +A 的矩阵,除非它们之一为标量。标量可以与任何一个矩阵相加 - A-B 减法或者一元运算符负号。A-B 把矩阵 A 减去 B。A 和 B 必须是具有相同长度的矩 -A 阵,除非它们之一为标量。标量可以被任何一个矩阵减去 .* A.*B 元素相乘。A.*B 相当于 A 和 B 对应的元素相乘。对于非标量的矩阵 A 和 B,矩阵 A 列长度必须和矩阵 B 的行长度一致。一个标量可以与任何一个矩阵相乘 ./ A./B 元素的右除法。矩阵 A 除以矩阵 B 的对应元素,即等于 A(i,j)/B(i,j)。对于非标量的 矩阵 A 和 B,矩阵 A 列长度必须和矩阵 B 的行长度一致 .\ A.\B 元素的左除法。矩阵 B 除以矩阵 A 的对应元素,即等于 B(i,j)/A(i,j)。对于非标量的 矩阵 A 和 B,矩阵 A 列长度必须和矩阵 B 的行长度一致 .^ A.^B 元素的乘方。等于[A(i,j)^B(i,j)],对于非标量的矩阵 A 和 B,矩阵 A 列长度必须和 矩阵 B 的行长度一致 .’ A.’ 矩阵转秩。当矩阵是复数时,不求矩阵的共轭 * A*B 矩阵乘法。对于非标量的矩阵 A 和 B,矩阵 A 列长度必须和矩阵 B 的行长度一致。 一个标量可以与任何一个矩阵相乘 / A/B 矩阵右除法。粗略地相当于 B*inv(A),准确地说相当于(A'\B')'。方程 X*A=B 的解 \ A\B 矩阵左除法。粗略地相当于 inv(A)*B。方程 A*X=B 的解 ^ A^B 矩阵乘方。具体用法参见表后面的补充说明 ’ A’ 矩阵转秩。当矩阵是复数时,求矩阵的共轭转秩 补充说明符号^的用法如下: 当 A 和 B 都是标量时,表示标量 A 的 B 次方幂。当 A 为方阵,B 为正整数时,表示矩阵 A 的 B 次乘积;B 为负整数时,表示矩阵 A 的逆的 B 次乘积。当 B 为非整数时,有如下表达 式: λ1B  .    A^B=V * .  /V    .   λnB  其中 λ1B ,L , λnB 为矩阵 A 的特征值,V 为对应的特征向量矩阵。当 A 为标量,B 为方阵 时,有如下表达式:  Aλ1    .   A^B=V * .  /V    .   Aλn  其中 Aλ1 ,L , Aλn 为方阵 B 的特征值,V 为对应的特征向量矩阵。当矩阵 A 和矩阵 B 都 为矩阵时,此运算无定义。 除了某些矩阵运算符,MATLAB 7.0 的算数运算符只对相同规模的数组作相应的运算。 –47– MATLAB 7.0 从入门到精通 对于向量和矩阵,两个操作数必须同规模或者有一个操作数为标量。如果一个操作数是标量, 而另外一个不是,MATLAB 7.0 会将这个标量与另一个操作数的每一个元素进行运算。 例如: A = magic(3) 上述语句得到矩阵如下: A= 816 357 492 在命令行输入语句如下: 3*A 上述语句得到输出代码如下: ans = 24 3 18 9 15 21 12 27 6 与以往的 MATLAB 版本不同的是,MATLAB 7.0 的数学运算符不但支持双精度数据类型 的运算,还增加了对单精度类型、1 字节无符号整数、1 字节有符号整数、2 字节无符号整数、 2 字节有符号整数、4 字节无符号整数和 4 字节有符号整数运算的支持。 2.3.2 关系运算符 MATLAB 7.0 的关系运算符的用法和功能如表 2-13 所示。 表 2-13 关系运算符 运算符 功能描述 < 小于 <= 小于等于 > 大于 >= 大于等于 == 等于 ~= 不等于 MATLAB 7.0 的关系运算符只对具有相同规模的两个操作数或者其中一个操作数为标量 的操作数进行操作。当两个操作数具有相同规模时,MATLAB 7.0 对两个矩阵的对应元素进 行比较,返回的结果是与操作数具有相同规模的矩阵。例如,下面的示例代码比较 3×3 魔方 矩阵中哪些元素的值大于 4。 magic(3)>4*ones(3) 上述语句得到输出代码如下: ans = 101 011 010 –48– 第 2 章 MATLAB 基础知识 返回结果中等于 1 的位置上,表示此处 magic(3)的矩阵元素大于 4。 当其中一个操作数是标量时,MATLAB 7.0 将标量与另一个操作数的每一个元素进行比 较,返回结果是与非标量操作数具有相同规模的矩阵。例如,上面的示例也可以用矩阵与标 量进行关系运算来实现,语句如下: magic(3)>4 当检验一个矩阵是否是空矩阵时,或许会想到用关系式 A==[]。但是如果 A 不是 0×0 或 1×1 的话,将返回警告信息。MATLAB 7.0 提供了一个函数 isempty()专门用于判断一个矩阵 是否为空矩阵,例如要检验 A 是否为空矩阵,可以用语句 isempty(A)。如果 A 为空矩阵则返 回 1,否则返回 0。 2.3.3 逻辑运算符 MATLAB 7.0 提供 3 种类型的逻辑运算符,即元素方式逻辑运算符、比特方式逻辑运算 符和短路逻辑运算符。 元素方式逻辑运算符的用法和功能如表 2-14 所示。元素方式逻辑运算符只接受逻辑类型 变量输入。表中例子采用如下矩阵: A = [0 1 1 0 1]; B = [1 1 0 0 1]; 表 2-14 元素方式逻辑运算符 运算符 & | ~ xor 功能描述 逻辑与。两个操作数同时为 1,运算结果为 1;否则为 0 逻辑或。两个操作数同时为 0,运算结果为 0;否则为 1 逻辑非。当 A 为 0 时,运算结果为 1;否则为 0 逻辑异或。但两个操作数相同时,运算结果为 0;否则为 1 例子 A&B = 01001 A|B = 11101 ~A = 10010 xor(A,B)=10100 MATLAB 7.0 的元素方式逻辑运算符只对具有相同规模的两个操作数或者其中一个操作 数为标量的操作数进行操作。 元素方式逻辑运算符有重载的函数,实际上符号' & '、' | '和 ' ~ '的重载函数分别是 and()、 or()和 not()。 比特方式逻辑运算符对操作数的每一个比特位进行逻辑操作,其用法和功能如表 2-15 所 示。比特方式逻辑运算符接受逻辑类型和非负整数变量输入。 表中例子采用如下矩阵: A = 28; % binary 11100 B = 21; % binary 10101 表 2-15 函数名 bitand 比特方式逻辑运算符 功能描述 位与。返回两个非负整数的对应位相与操作 bitor 位或。返回两个非负整数的对应位做或操作 bitcmp 补码。返回 n 位整数表示的补码 bitxor 位异或。返回两个非负整数的对应位做异或操作 例子 bitand(A,B) = 20 (binary 10100) bitor(A,B) = 29 (binary 11101) bitcmp(A,5) = 3 (binary 00011) bitxor(A,B) = 9 (binary 01001) –49– MATLAB 7.0 从入门到精通 MATLAB 7.0 的短路逻辑操作符用法和功能如表 2-16 所示。 表 2-16 短路逻辑运算符 运算符 功能描述 && 逻辑与。两个操作数同时为 1,运算结果为 1;否则为 0 || 逻辑或。两个操作数同时为 0,运算结果为 0;否则为 1 短路逻辑运算符的运算结果和元素方式逻辑运算符的运算结果是一样的。然而短路逻辑 运算符在执行时,只有在运算结果还不确定时才去参考第二个操作数。例如,A&&B 操作, 当 A 为 0 时,直接返回 0,而不检查 B 的值;当 A 为 1 时,如果 B 为 1,则返回 1,否则返 回 0。A||B 的执行方式也与 A&&B 类似。 有些情况下,需要满足特定条件,才能被执行的语句,可以用短路逻辑运算符来实现。 例如,下面语句实现判断是否 a/b>1,当 b 为 0 时也不会出错。当 b=0 时,函数直接返回 0, 而不计算 a/b。 x = (b ~= 0) && (a/b > 1) 2.3.4 运算优先级 用户编写的表达式可以包括算术运算符、关系运算符和逻辑运算符。因此运算符的优先 级决定了对一个表达式的求值顺序。具有相同优先级的运算符则从左到右依次进行运算;不 同优先级的运算符则先进行高优先级运算。运算符的优先等级如表 2-17 所示。 表 2-17 运算符的优先等级 运算符 优先等级 括号 最高优先级 转秩(.’),幂(.^),复共轭转秩(’),矩阵幂(^) 一元正号(+),一元负号(-),逻辑非() 元素相乘(.*),元素右除(./),元素左除(.\),矩阵乘法(*),矩阵右除(/),矩 阵左除(/) 加法(+),减法(-) 冒号运算符(:) 小于(<),小于等于(<=),大于(>),大于等于(>=),等于(==),不等于(~=) 逻辑与(&) 逻辑或(|) 短路逻辑与(&&) 短路逻辑或(||) 最低优先级 由表 2-17 中可以看到,括号的优先级别最高,因此可以用括号来改变默认的优先等级, 例如下面的示例代码: A = [3 9 5]; B = [2 1 5]; C = A./B.^2 上述语句得到输出代码如下: C= 0.7500 9.0000 0.2000 而采用括号后的语句如下: –50– C = (A./B).^2 上述语句输出结果代码如下: C= 2.2500 81.0000 1.0000 第 2 章 MATLAB 基础知识 2.4 字符串处理函数 本节介绍字符串处理函数。字符串是指 1×n 的字符数组。 MATLAB 7.0 能够很好地支持字符串数据,用户可以用两个不同的方式来表示字符串, 即字符数组和字符串单元数组。用户可以用 m×n 的字符数组来表示多个字符串,只要这些字 符串的长度是一样。当需要保存多个不同长度的字符串时,可以用单元类型来实现。 MATLAB 7.0 提供了很多字符串操作,包括字符串的创建、合并、比较、查找以及与数 值之间的转换。 2.4.1 字符串的构造 1.创建字符数组 可以用一对单引号来表示字符串,例如下面的示例代码: str= 'I am a great person '; 也可以用字符串合并函数 strcat()来得到一个新的字符串,例如下面的示例代码: a='My name is '; b='Clayton Shen'; c=strcat(a,b) 上述语句得到输出代码如下: c= My name isClayton Shen 值得注意的是,函数 strcat()在合并字符串的同时会把字符串结尾的空格删除。要保留这 些空格,可以用矩阵合并符[]来实现字符串合并,例如下面的示例代码: a='My name is '; b='Clayton Shen'; c=[a b] 上述语句得到输出代码如下: c= My name is Clayton Shen 用户也可以构造二维字符数组,不过要注意保持二维字符数组的每一行具有相同的长 度。例如,下面字符串是合法的,因为它的每行都有 6 个字符: str=['second';'string'] 上述语句得到输出代码如下: –51– MATLAB 7.0 从入门到精通 str = second string 当构造的多个字符串具有不同长度时,可以在字符串的尾部添加空格来强制实现字符串 具有相同的长度。 例如下面的示例代码: str=['name ';'string'] 上述语句得到输出代码如下: str = name string 一个更简单的方法是利用函数 char()来创建字符串。该函数创建字符串数组时,如果字 符串不具有相同的长度,则函数 char()自动用空格把字符串补足到最长的字符串的长度。例 如下面的示例代码: c=char('first','second') 上述语句得到输出代码如下: c= first second 与函数 char()具有类似功能的函数是 strvcat()。函数 strvcat()把多个字符串合并为一个字 符串数组。当字符串不具有相同长度时,函数 strvcat()自动在尾部添加空格补足到最长的字 符串的长度。例如下面的示例代码: c=strvcat('name','string') 上述语句得到输出代码如下: c= name string 2.创建字符串单元数组 可以用函数 cellstr()来创建字符串单元数组。例如,有一个字符数组如下: data=['name ';'string'] 上述语句得到输出代码如下: data = name string 下面语句的功能是把上述字符数组转换成为字符串单元数组,示例代码设置如下: celldata=cellstr(data) 上述语句得到输出代码如下: celldata = 'name' –52– 第 2 章 MATLAB 基础知识 'string' 注意,函数 cellstr()已经把字符串尾部的空格截去。可以查看 celldata 的第一个字符串长 度如下: length(celldata{1}) 上述语句得到输出代码如下: ans = 4 可以用函数 char()把一个字符串单元数组转换成一个字符数组,示例代码设置如下: chararray=char(celldata) 上述语句得到输出代码如下: chararray = name string 其第一个字符串的长度可以得到如下的代码: length(chararray(1,:)) 上述语句得到输出代码如下: ans = 6 2.4.2 字符串比较函数 MATLAB 7.0 里包括以下几种比较字符串和字符子串的方法。比较两个字符串或者两个 字符串的子串是否相同。 • 比较两个字符串中的单独字符是否相同; • 对字符串内的元素进行识别,判定每一个元素是字符还是空白符(包括空格、制表符 Tab 和换行符)。 这些函数对字符数组和字符串数组都适用。 1.字符串比较函数 MATLAB 7.0 为用户提供的字符串比较函数如表 2-18 所示。 表 2-18 字符串比较函数 函数名 功能描述 基本调用格式 strcmp strncmp 比较两个字符串是否相等 比较两个字符串的前 n 个字符是否相 等 strcmp(S1,S2) 如果字符串相等则返回 1,否则返回 0 strncmp(S1,S2,N) 如果字符串的前 N 个字符相等则 返回 1,否则返回 0 strcmpi 与 strcmp 函数功能相同,只是忽略字 strcmpi(S1,S2) 如果字符串相等则返回 1,否则返 符串的大小写 回 0(忽略大小写) d strncmpi 与 strncmp 函数功能相同,只是忽略 strncmpi(S1,S2,N) 如果字符串的前 N 个字符相等 字符串的大小写 则返回 1,否则返回 0(忽略大小写) 例如,有两个字符串如下: str1='blink'; –53– MATLAB 7.0 从入门到精通 str2='bliss'; 由于这两个字符串不相同,故调用函数 strsmp()的结果为 0,语句如下: c=strcmp(str1,str2) 上述语句得到输出代码如下: c= 0 这两个字符串的前 3 个字符是相同的,故用函数 strncmp()比较它们的前 3 个字符,会返 回 1,代码设置如下: c=strncmp(str1,str2,3) 上述语句得到输出代码如下: c= 1 2.用关系运算符比较字符串 可以对字符数组运用 MATLAB 7.0 的关系运算符,但是要求比较的字符数组具有相同的 维数,或者是其中一个是标量。例如,可以用等号运算符(==)来判断两个字符串里哪些字 符是相同的。 str1='carnal'; str2='casual'; c=str1==str2 上述语句得到输出代码如下: c= 110011 当然,还可以用其他关系运算符(>、>=、<、<=、==、!=)来比较两个字符串。 2.4.3 字符串查找和替换函数 MATLAB 7.0 为用户提供的一般字符串查找和替换函数如表 2-19 所示。 表 2-19 函数名 strrep findstr strfind strtok 功能描述 字符串替换 字符串内查找(两 个输入对等) 字符串内查找 获得第一个分隔符 之前的字符串 字符串查找和替换函数 基本调用格式 str = strrep(str1, str2, str3) 把 str1 中的 str2 子串替换成 str3 k = findstr(str1,str2) 查找输入中较长的字符串中较短字符串 的位置 k = strfind(str, pattern) 查找 str 中 pattern 出现的位置 k = strfind(cellstr, pattern) 查找单元字符串 cellstr 中 pattern 出现的 位置 token = strtok('str') 换行符)为分隔符 以空格符(包括空格、制表符(Tab)和 token = strtok('str', delimiter) 输入 delimiter 为指定的分隔符 [token, rem] = strtok(...) 返回值 rem 为第一个分隔符之后的字符 串 –54– 第 2 章 MATLAB 基础知识 续表 函数名 功能描述 基本调用格式 strmatch 在字符串数组中匹 配指定字符串 x = strmatch('str', STRS) 在字符串数组 STRS 中匹配字符串 str, 返回匹配上的字符串所在行的指标 x = strmatch('str', STRS, 'exact') 在字符串数组 STRS 中精确匹配字符 串 str,返回匹配上的字符串的所在行指标。只有完全匹配上时,才 返回字符串的行指标 下面例子是实现字符串替换: s1 = 'This is a good example.'; str = strrep(s1, 'good', 'great') 上述语句得到输出代码如下: str = This is a great example. 下面例子是用于实现字符串查找: str = 'This is a good example.'; index = strfind(str, 'a') 上述语句得到输出代码如下: index = 9 18 下面例子是用于获得第一个分隔符之前的字符串: s = ' This is a simple example.'; [token, rem] = strtok(s) 上述语句得到输出代码如下: token = This rem = is a simple example. 下面例子是用于实现字符串匹配: x = strmatch('max', strvcat('max', 'minimax', 'maximum')) 上述语句得到输出代码如下: x= 1 3 2.4.4 字符串——数值转换 MATLAB 7.0 为用户提供的把数值转换为字符串的函数如表 2-20 所示。 表 2-20 函数名 char int2str 数值转换为字符串的函数 功能描述 把一个数值截取小数部分,然后转换为等值的字符 把一个数值的小数部分四舍五入,然后转换为字符串 [72 105] [72 105] 例子 → 'Hi' → '72 105' –55– MATLAB 7.0 从入门到精通 续表 函数名 功能描述 例子 num2str mat2str dec2hex dec2bin dec2base 把一个数值类型的数据转换为字符串 [72 105]' → 格式为%1d/) 把一个数值类型的数据转换为字符串,返回的结果是 MATLAB 7.0 能识别的格式 [72 105]' → 把一个正整数转换为十六进制的字符串表示 [72 105] → 把一个正整数转换为二进制的字符串表示 [72 105] → 1101001' 把一个正整数转换为任意进制的字符串表示 [72 105] → (八进制) 72/105/' (输出 [72 105]' '48 69' '1001000 '110 151' MATLAB 7.0 为用户提供的把字符串转换为数值的函数如表 2-21 所示。 表 2-21 字符串转换为数值的函数 函数名 功能描述 例子 uintN str2num str2double 把字符转换为等值的整数 把一个字符串转换为数值类型 与 str2num 相似,但比 str2num 性能优越,同时提供对单 元字符数组的支持 'Hi' → [72 105] '72 105' → [72 105] {'72' '105'} → [72 105] hex2num 把一个 IEEE 格式的十六进制字符串转换为数值类型 '400921fb54442d18' → 'pi' hex2dec bin2dec base2dec 把一个 IEEE 格式的十六进制字符串转换为整数 把一个二进制字符串转换为十进制整数 把一个任意进制的字符串转换为十进制整数 '12B' → 299 ' 010111 ' → 23 '12' → 10(八进制) 例如,要在命令行中输出一行字符串来显示向量 x 的最大值,可以用如下程序: x=rand(1,10); disp(['向量 x 中的最大值为:' num2str((max(x)))]); 其中函数 disp()是在命令行中显示一个字符串。由上述语句得到的输出代码如下: 向量 x 中的最大值为:0.93547 –56– 第 3 章 数学运算 本章将介绍 MATLAB 7.0 中与数学运算有关的函数和概念。在 MATLAB 7.0 中一切数据 均以矩阵的形式出现,它的数学运算则包括两种:一种是针对整个矩阵的数学运算,称之为 矩阵运算,例如求矩阵的行列式的函数 det();另一种是针对矩阵的每一个元素进行运算的函 数,称之为矩阵元素的运算,例如求矩阵每一元素的正弦值的函数 sin()。 本章 3.1 小节中将介绍矩阵运算函数,包括矩阵分析、线性方程组、矩阵分解、矩阵的 特征值和特征向量,以及非线性矩阵运算。3.2 小节将介绍矩阵元素的运算函数,包括三角函 数、指数对数函数、复数函数和截断求余函数。3.3 小节介绍特殊数学函数,这些函数应该属 于矩阵元素的运算,特殊数学函数包括特殊函数、数论函数和坐标变换函数。由于特殊数学 函数在使用和应用场合与 3.2 小节介绍的函数差异较大,故单独做一小节介绍。 3.1 矩阵运算 矩阵运算是线性代数中极其重要的部分。MATLAB 7.0 可以支持很多线性代数中定义的 操作。MATLAB 7.0 强大的矩阵运算能力使其成为优秀数值计算软件。 本小节将介绍与矩阵运算相关的内容,包括矩阵分析、线性方程组求解、特征值求解和 奇异值等。 3.1.1 矩阵分析 MATLAB 7.0 提供的矩阵分析函数如表 3-1 所示。 表 3-1 函数名 norm normest rank det trace null orth rref subspace 矩阵分析函数 求矩阵或者向量的范数 估计矩阵的 2 阶范数 矩阵的秩,即求对角元素的和 矩阵的行列式 矩阵的迹 0 空间 正交化空间 约化行阶梯形式 求两个矩阵空间的角度 功能描述 1.向量和矩阵的范数运算 对于线性空间中的一个向量 x={x1,x2,...xn},如果存在一个函数 ρ (x) 满足以下 3 个条件: MATLAB 7.0 从入门到精通 (1) ρ(x) >0,且 ρ(x) =0 的充要条件为 x=0; (2) ρ (ax) = a ρ (x) ,其中 a 为任意标量; (3)对向量 x 和 y,有 ρ (x+y) ≤ ρ (x) + ρ( y) 。 称 ρ (x) 为向量 x 的范数,一般记为 x 。范数的形式多种多样,下面式子中定义的范数 操作就满足以上 3 个条件: ∑ max min x p =   n i =1 xi p 1/  p , p = 1, 2,L ,且 x ∞ = 1≤i≤n xi ; x −∞ = 1≤i≤n xi 上式定义的 x 称为 p 阶范数,其中最有用的是 1、2 和 ∞ 阶范数。 p 矩阵的范数是基于向量的范数定义的,其定义式如下: Ax A = max x≠0 x 与向量的范数一样,矩阵的范数最常用的也是 1、2 和 ∞ 阶范数。它们的定义如下: n n max∑ max∑ A = 1 aij , A= 2 Smax{AT A} 和 A ∞ = aij 1≤ j≤n i=1 1≤i≤n j=1 在上式中 S(A)为矩阵 A 的特征值,而 Smax{AT A} 则为 A 矩阵的最大奇异值的平方。 在 MATLAB 7.0 中,求向量范数的函数具体用法如下: • N=norm(x,p) 对任意大于 1 的 p 值,返回向量 x 的 p 阶范数; • N=norm(x) 返回向量的 2 阶范数,相当于 N=norm(x,2); • N=norm(x,inf) 返回向量的 ∞ 阶范数,相当于 N=max(abs(x)); • N=norm(x,-inf) 返回向量的- ∞ 阶范数,相当于 N=min(abs(x))。 在 MATLAB 7.0 中,求矩阵范数的函数具体用法如下: • N=norm(A) 计算矩阵的 2 阶范数,也就是最大奇异值; • N=norm(A,p) 根据参数 p 的值不同,求不同阶的范数值。当 p=1 时,计算矩阵 A 的 1 阶范数,相当于 max(sum(abs(A)))。当 p=2 时,计算矩阵 A 的 2 阶范数,相当于 norm(A)。p=inf 时,计算矩阵 A 的 ∞ 阶范数,相当于 max(sum(abs(A’)))。当 p=’pro’时, 计算矩阵 A 的 F 范数(Frobenius 范数),相当于 sqrt(sum(diag(A’*A)))。 例如求向量 x 的 2 阶范数,具体代码如下: norm(1:5,2) 由上述语句得到具体代码如下: ans = 7.4162 –58– 第 3 章 数学运算 例如求单位矩阵的 1 阶范数,具体代码如下: norm(eye(3),1) 由上述语句得到具体代码如下: ans = 1 当矩阵维数比较大时会导致计算矩阵范数的时间比较长,并且当一个近似的范数值满足 要求时,可以考虑使用函数 normest()来估计 2 阶范数值。函数 normest()最初开发时是为了提 供给稀疏矩阵使用的,同时它也能接受满矩阵的输入,一般在满矩阵维数比较大时使用。 函数 normest()的用法如下: • normest(S) 估计矩阵 S 的 2 阶范数值,默认允许的相对误差为 1e-6; • normest(S,tol) 使用 tol 作为允许的相对误差。 例如,首先用函数生成一个 1000×1000 的柯西矩阵 W,该柯西矩阵满足条件 Wij = 1/(xi+yj),其中 xi!=xj, yi!=y(j 当 i!=j),然后对该矩阵 W 求其 2 阶范数,最后对比函数 norm() 和 normest()的效果,具体代码如下: W = gallery(’cauchy’,1000); %产生一个 1000×1000 的柯西矩阵 t1=clock; %获得系统时间 W_norm=norm(W); %计算范数 t2=clock; %获得系统时间 t_norm=etime(t2,t1) %计算范数的耗时 t3=clock; %获得系统时间 W_normest=normest(W); %估计范数 t4=clock; %获得系统时间 t_normest=etime(t4,t3) %估计范数的耗时 由上述语句得到如下代码: W_norm = 2.2469 t_norm = 5.7660 W_normest = 2.2469 t_normest = 0.1410 由上述示例可以看出,函数 norm()计算出的范数精确值为 2.2469,耗时 5.7660s,而函数 normest()计算出的范数值也为 2.2469,但耗时为 0.1410s。这就说明了,采用函数 normest() 计算范数可以大大提高计算速度。 2.矩阵的秩 矩阵 A 中线性无关的列向量个数称为列秩,线性无关的行向量个数称为行秩。可以证明 矩阵的列秩与行秩是相等的。矩阵求秩的方法很多,其中有些算法是稳定的,有些算法是不 稳定的。MATLAB 7.0 中用函数 rank()来计算矩阵的秩,所采用的算法是基于矩阵奇异值分 –59– MATLAB 7.0 从入门到精通 解基础上的,具体代码如下: s = svd(A); tol = max(size(A))*eps(max(s)); r = sum(s > tol) 这种算法是最耗时,同时也是最稳定的。 函数 rank()的用法如下: • rank(A) 用默认允许误差计算矩阵的秩; • rank(A,tol) 给定允许误差计算矩阵的秩,tol = max(size(A)) * eps(norm(A))。 例如,求 6 阶单位矩阵的秩,具体代码如下: rank(eye(6)) 由上述语句得到如下代码: ans = 6 3.矩阵的行列式 矩阵 A={aij}n×n 的行列式定义如下: ∑ A = det( A) = (−1)k a1k1a2k2 L ankn ,其中 k1, k2 ,L , kn 是将序列1,2,L , n 交换 k 次所 k 得的序列。 在 MATLAB 7.0 中用函数 det()来计算矩阵的行列式。 例如,计算矩阵 A=[1 2 3;4 5 6;7 8 9]的行列式,如下: A=[1 2 3;4 5 6;7 8 9]; A_det=det(A); disp([’A 的行列式值=’,num2str(A_det)]); 由上述语句得到如下代码: A 的行列式值=0 矩阵 A 的行列式恰好为 0。 线性代数中定义行列式为 0 的矩阵为奇异矩阵。但是一般不能使用语句 abs(det(A))<=ε 来判断矩阵 A 的奇异性,因为不容易选择合适的允许误差ε来满足要求。MATLAB 7.0 提供函 数 cond()可以用来判定矩阵的奇异性。 4.矩阵的迹 矩阵的迹定义为矩阵对角元素之和。在 MATLAB 7.0 中用函数 trace()来计算矩阵的迹。 例如,求矩阵 A=[1 2 3;4 5 6;7 8 9]的迹,如下: A=[1 2 3;4 5 6;7 8 9]; A_trace=trace(A); disp([’A 的迹 = ’,num2str(A_trace)]); 由上述语句得到代码如下: A 的迹 = 15 –60– 5.矩阵的化零矩阵 第 3 章 数学运算 如果对于非满秩矩阵 A,若有矩阵 Z 使得 A*Z 的元素都为 0,且矩阵 Z 为一个正交矩阵 (Z’*Z=I),则称矩阵 Z 为矩阵 A 的化零矩阵。MATLAB 7.0 中提供了求化零矩阵的函数 null(), 用法如下: • Z = null(A) 返回矩阵 A 的一个化零矩阵,如果化零矩阵不存在则返回空矩阵; • Z = null(A,’r’) 返回有理数形式的化零矩阵。 例如,求矩阵 A=[1 2 3;1 2 3;4 5 6]的化零矩阵,具体代码设置如下: A=[1 2 3;1 2 3;4 5 6]; Z=null(A) AZ=A*Z 由上述语句得到代码如下: Z= -0.4082 0.8165 -0.4082 AZ = 1.0e-015 * 0.2220 0.2220 -0.8882 如果要求矩阵 A 的有理数形式的化零矩阵,具体代码设置如下: ZR = null(A,’r’) AZR=A*ZR 由上述语句得到代码如下: ZR = 1 -2 1 AZR = 0 0 0 6.矩阵的正交空间 矩阵 A 的正交空间 Q 具有 Q’*Q=I 的性质,并且 Q 的列矢量构成的线性空间与矩阵 A 的列矢量构成的线性空间相同,且正交空间 Q 与矩阵 A 具有相同的秩。MATLAB 7.0 中提供 了函数 orth()来求正交空间 Q。 • Q = orth(A),返回矩阵 A 的正交空间 Q 例如,求出矩阵 A 的正交空间 Q,并比较 Q 和 A 的秩,具体代码如下: –61– MATLAB 7.0 从入门到精通 A=[1 2 3;4 5 6;7 8 9;10 11 12]; %矩阵 A Q=orth(A) %正交矩阵 Q t=(rank(A)==rank(Q)) %比较矩阵 A 与 Q 的秩 由上述语句得到代码如下输出: Q= -0.1409 0.8247 -0.3439 0.4263 -0.5470 0.0278 -0.7501 -0.3706 t= 1 7.矩阵的约化行阶梯形式 矩阵的约化行阶梯形式是高斯-约旦消去法解线性方程组的结果,其形式为: 1 K   M 0 O L 0 * M 1 ** MATLAB 7.0 中提供了函数 rref()来求矩阵的约化行阶梯形式,其用法如下: • R = rref(A),返回矩阵 A 的约化行阶梯形式 R。 • [R,jb] = rref(A),返回矩阵 A 的约化行阶梯形式 R,同时返回 1×r 的向量 jb 使 r 为矩阵 A 的秩;A(:,jb)是矩阵 A 的列矢量构成的线性空间;R(1:r,jb)是 r×r 的单位矩阵。 • [R,jb] = rref(A,tol),以 tol 作为误差容限计算矩阵 A 的秩。 例如,求出矩阵 A 的约化行阶梯形式,并比较 Q 和 A 的秩,具体代码设置如下: A=[1 2 3;4 5 6;7 8 9;10 11 12]; %矩阵 A R=rref(A) %正交矩阵 A 的约化行阶梯形式 t=(rank(A)==rank(R)) %比较矩阵 A 与矩阵 R 的秩 由上述语句得到如下代码: R= 1 0 -1 012 000 000 t= 1 8.矩阵空间之间的夹角 矩阵空间之间的夹角代表两个矩阵线性相关的程度,如果夹角很小,它们之间线性相关 度就很高,反之,它们之间线性相关度就不大。在 MATLAB 7.0 中用函数 subspace()来实现 –62– 求矩阵空间之间的夹角,其调用格式如下。 • theta = subspace(A,B),返回矩阵 A 和矩阵 B 之间的夹角。 例如,求矩阵 A 和 B 之间的夹角,代码如下: A=[1 2 3;4 5 6;7 8 9;10 11 12]; %矩阵 A B=magic(4); subspace(A,B) 上述语句得到结果如下: ans = 0.6435 该结果表明矩阵 A 和矩阵 B 的线性相关度很低。 3.1.2 线性方程组 第 3 章 数学运算 1.线性方程组的问题 在工程计算中,一个重要的问题是线性方程组的求解。在矩阵表示法中,上述问题可以 表述为给定两个矩阵 A 和 B,是否存在惟一的解 X 使得 AX=B 或 XA=B。 例如,求解方程 3x=6 就可以将矩阵 A 和 B 看成是标量的一种情况,最后得出该方程的 解为 x=6/3=2。 尽管在标准的数学中并没有矩阵除法的概念,但 MATLAB 7.0 采用了与解标量方程中类 似的约定,用除号来表示求解线性方程的解。MATLAB 7.0 采用第 2 章介绍过的运算符斜杠 ’/’和运算符反斜杠’\ ’来表示求线性方程的解,其具体含义如下: • X=A\B 表示求矩阵方程 AX=B 的解; • X=B/A 表示求矩阵方程 XA=B 的解。 对于 X=A\B,要求矩阵 A 和矩阵 B 有相同的行数,X 和 B 有相同的列数,X 的行数等于 矩阵 A 的列数。X=B\A 行和列的性质则与之相反。 在实际情况中,形式 AX=B 的线性方程组比形式 XA=B 的线性方程组要常见得多。因此 反斜杠’\’用得更多。本小节的内容也主要针对反斜杠’\’除法进行介绍。斜杠’/’除法的性质可以 由恒等变换式得到(B/A)’=(A’\B’)。 系数矩阵 A 不一定要求是方阵,矩阵 A 可以是 m×n 的矩阵,有如下 3 种情况: • m=n 恰定方程组,MATLAB7.0 会寻求精确解; • m>n 超定方程组,MATLAB7.0 会寻求最小二乘解; • m chol Matrix must be positive definite. 但如果采用 Cholesky-Infinity 分解则不会出错,具体代码如下: Rinf = cholinc(sparse(S),’inf’); Rinf=full(Rinf) 由上述语句得到如下代码: Rinf = 1030 0506 0 0 Inf 0 0 0 0 25 其中,Rinf’* Rinf 并不等于矩阵 S。 第 3 章 数学运算 2.一般方阵的高斯消去法 高斯消去法又称 LU 分解,它可以将任意一个方阵 A 分解为一个“心理”下三角矩阵 L 和一个上三角矩阵 U 的乘积,即 A=LU。“心理”下三角矩阵定义为下三角矩阵与置换矩阵 的乘积。 LU 分解在 MATLAB 7.0 中用函数 lu()来实现,其调用方式如下。 • [L,U] = lu(X),X 为一个方阵,L 为“心理”下三角矩阵,U 为上三角矩阵,满足关系 X=L*U。 • [L,U,P] = lu(X),X 为一个方阵,L 为下三角矩阵,U 为上三角矩阵,P 为置换矩阵, 满足关系 P*X = L*U。 • Y = lu(X),X 为一个方阵。把上三角矩阵和下三角矩阵合并在矩阵 Y 中给出,矩阵 Y 的对角元素为上三角矩阵的对角元素,也即 Y=L+U-I。置换矩阵 P 的信息丢失。 考虑线性方程组 AX=B,其中,对矩阵 A 可以做 LU 分解,使得 A=L*U,这样线性方程 组就可以改写成 L*U*X=B,由于左除算符’\’可以快速处理三角矩阵,因此可以快速解出: X=U\(L\B) 矩阵的行列式的值和矩阵的逆也可以利用 LU 分解来计算,示例如下: det(A)=det(L)*det(U) inv(A)=inv(U)*inv(L) 例如,做矩阵的 LU 分解,具体代码如下: A=[1 4 2;5 6 9;4 1 8]; [L,U,P]=lu(A) 由上述语句得到如下代码: L= 1.0000 0 0 0.8000 1.0000 0 0.2000 -0.7368 1.0000 U= –69– MATLAB 7.0 从入门到精通 5.0000 6.0000 9.0000 0 -3.8000 0.8000 0 0 0.7895 P= 010 001 100 对于稀疏矩阵,MATLAB 7.0 提供了函数 luinc()来做不完全 LU 分解,其调用格式如下。 • [L U]= luinc(X,DROPTOL),其中 X、L 和 U 的含义与函数 lu()中的变量相同,DROPTOL 为不完全 LU 分解的丢失容限。当 DROPTOL 设为 0 时,退化为完全 LU 分解。 • [L U] = luinc(X,OPTS),其中 OPTS 为结构体,它有 4 个属性,即 DROPTOL、MICHOL、 RDIAG 和 THRESH。DROPTOL 为不完全 LU 分解的丢失容限;MICHOL 为 1 时采用 改进算法的不完全 LU 分解,否则不采用改进算法;RDIAG 为 1 时,R 的对角元素中 的零值替换成为 DROPTOL 的平方根,为 0 时不做此替换;THRESH 是绕对角线旋转 因子,其值范围是[0,1],当 THRESH 为 0 时强制绕对角线旋转,THRESH 的默认值是 1。 • [L,U,P] = luinc(X,’0’),0 级不完全 LU 分解。 • [L,U] = luinc(X,’0’),0 级不完全 LU 分解。 • Y = luinc(X,’0’),0 级完全 LU 分解。 例如,计算一个稀疏矩阵的不完全分解,并观察其非零元素的个数,具体代码如下: load west0479; %加载 MATLAB 7.0 自带的一个稀疏矩阵 S = west0479; [L,U,P] = luinc(S,’0’); %做不完全 LU 分解 figure(1); %新开一个图形显示窗口 subplot(2,3,1); %把该窗口分成 2*3 个字窗口,并在第 1 个字窗口上画图 spy(S); %显示稀疏矩阵 S title(’S’); %给图添上标题 subplot(2,3,2); %在第 2 个字窗口上画图 spy(L); %显示稀疏矩阵 L title(’L’); %给图添上标题 subplot(2,3,3); %在第 2 个字窗口上画图 spy(U); %显示稀疏矩阵 U title(’U’); %给图添上标题 subplot(2,3,4); %在第 2 个字窗口上画图 spy(P*S); %显示稀疏矩阵 P*S title(’P*S’); %给图添上标题 subplot(2,3,5); %在第 2 个字窗口上画图 spy(L*U); %显示稀疏矩阵 L*U title(’L*U’); %给图添上标题 由上述代码得到如图 3-2 所示的结果。由函数 luinc()得到上三角矩阵 U 和下三角矩阵 L, –70– 并且 P*S 与 L*U 有相似的稀疏结构。 第 3 章 数学运算 图 3-2 不完全 LU 分解结果 3.矩形矩阵的正交分解 矩形矩阵的正交分解又称 QR 分解。QR 分解把一个 m×n 的矩阵 A 分解为一个正交矩阵 Q 和一个上三角矩阵 R 的乘积,即 A=Q*R。 在 MATLAB 7.0 中 QR 分解由函数 qr()来实现,下面介绍 QR 分解的调用方式。 • [Q,R] = qr(A),其中矩阵 R 为与矩阵 A 具有相同大小的上三角矩阵,Q 为正交矩阵。 它们满足 A=Q*R。该调用方式适用于满矩阵和稀疏矩阵。 • [Q,R] = qr(A,0),为“经济”方式的 QR 分解。设矩阵 A 是一个 m×n 的矩阵,若 m>n, 则只计算矩阵 Q 的前 n 列元素,R 为 n×n 的矩阵;若 m<=n,则与[Q,R] = qr(A)效果一 致。该调用方式适用于满矩阵和稀疏矩阵。 • [Q,R,E] = qr(A),R 是上三角矩阵,Q 为正交矩阵,E 为置换矩阵。它们满足 A*E=Q*R, 程序选择一个合适的矩阵 E 使得 abs(diag(R))是降序排列的。该调用方式适用于满矩 阵。 • [Q,R,E] = qr(A,0),“经济”方式的 QR 分解,其中 E 是一个置换矢量。它们满足 A(:,E) = Q*R。该调用方式适用于满矩阵。 • R = qr(A),返回上三角矩阵 R,这里 R= chol(A’*A)。该调用方式适用于稀疏矩阵。 • R = qr(A,0),以“经济”方式返回上三角矩阵 R。 • [C,R] = qr(A,B),其中矩阵 B 必须与矩阵 A 具有相同的行数,矩阵 R 是上三角矩阵, C=Q’*B。 –71– MATLAB 7.0 从入门到精通 例如,通过 QR 分解分析矩阵的秩,具体代码如下: A= [1 2 3 456 789 10 11 12]; A_rank=rank(A); disp([’矩阵 A 的秩 = ’ num2str(A_rank)]); [Q,R]=qr(A) 由上述语句得到如下代码: 矩阵 A 的秩 = 2 Q= -0.0776 -0.8331 0.5473 -0.0221 -0.3105 -0.4512 -0.7133 0.4373 -0.5433 -0.0694 -0.2153 -0.8085 -0.7762 0.3124 0.3813 0.3932 R= -12.8841 -14.5916 -16.2992 0 -1.0413 -2.0826 0 0 -0.0000 0 0 0 矩阵 R 的第三行和第四行的元素为 0,可以看出矩阵 R 不满秩,从而得到矩阵 A 的秩为 2。该结果与用函数 rank()得到的结果一致。 4.奇异值分解 奇异值分解在矩阵分析中占有极其重要的地位,即对于 m×n 的矩阵 A,若存在 m×m 的 酉矩阵 U 和 n×n 的酉矩阵 V,使得 A=U*ƒ†*V’,其中 ƒ†为一个 m×n 的非负对角矩阵,并且其 对角元素的值按降序排列,则式子 A=U*ƒ†*V’即为矩阵 A 的奇异值分解,U、ƒ†和 V 称为矩 阵 A 的奇异值分解的三对组。 在 MATLAB 7.0 中奇异值分解由函数 svd()来实现,其调用方式如下。 • [U,S,V] = svd(X),奇异值分解。 • [U,S,V] = svd(X,0),“经济”方式的奇异值分解。设矩阵 X 的大小为 m×n,若 m>n,则 只计算 U 的前 n 列元素,S 为 n×n 的矩阵;若 m<=n,则与[U,S,V] = svd(X)效果一致。 • [U,S,V] = svd(X,’econ’),设矩阵 X 的大小为 m×n,若 m>=n,则与[U,S,V] = svd(X,0)效 果一致,若 m= abs(N)的最小正整 数 P,其中 N 为输入 pow2 以 2 为底的指数 例如,计算 ex 和 2x 的值,具体代码如下: x=-1:0.1:4; %x 值 figure(1); %新开一个画图窗口 plot(x,exp(x),’ro-’,x,pow2(x),’g+-’); %画图 xlabel(’x’); %标注横坐标 ylabel(’函数值’); %标注纵坐标 legend(’e^x’,’2^x’); %给图添加图例 由上述语句得到如图 3-5 所示的示意图。 图 3-5 指数函数示意图 3.2.3 复数函数 MATLAB 7.0 提供的复数函数及其功能如表 3-8 所示。 –81– MATLAB 7.0 从入门到精通 表 3-8 函数名 功能描述 复数函数 函数名 功能描述 abs 绝对值(复数的模) real 复数的实部 angle 复数的相角 unwrap 调整矩阵元素的相位 complex 用实部和虚部构造一个复数 isreal 是否为实数矩阵 conj 复数的共轭 cplxpair 把复数矩阵排列成为复共轭对 imag 复数的虚部 复数函数中除了函数 unwrap()和 cplxpair()的用法比较复杂外,其他函数都比较简单。下 面就详细介绍函数 unwrap()和 cplxpair()。 函数 unwrap()用于对表示相位的矩阵进行校正,当矩阵相邻元素的相位差大于设定阈值 (默认值为 π)时,函数 unwrap()通过加±2π来校正相位。函数 unwrap()的基本调用格式如下。 • Q = unwrap(P),当相位大于默认阈值 π时,矫正相位; • Q = unwrap(P,tol),用 tol 来设定阈值; • Q = unwrap(P,[],dim),用默认阈值 π在给定维 dim 上做相位矫正; • Q = unwrap(P,tol,dim),用阈值 tol 在给定维 dim 上做相位矫正。 例如,下面数据是滤波器的相位响应,其中 w 为频率,p 为相位。下面程序将比较数据 校正前和程序后的差别,具体代码如下: w = 1000*[0.2:.2:3,3.5:1:10]; %频率 p = [-1.5728 -1.5747 -1.5772 -1.5790 -1.5816 -1.5852 -1.5877 -1.5922 ... -1.5976 -1.6044 -1.6129 -1.6269 -1.6512 -1.6998 -1.8621 1.7252 ... 1.6124 1.5930 1.5916 1.5708 1.5708 1.5708 ]; %相位 figure(1); %开一个新的画图窗口 semilogx(w,p,’ro-’); %用半对数坐标画原始数据 hold on; %保持原来的图 semilogx(w,unwrap(p),’b*-’); %用半对数坐标画校正后数据 xlabel(’频率’); %添加横坐标说明 ylabel(’相位’); %添加纵坐标说明 legend(’原始相频特性曲线’,’矫正后的相频特性曲线’); %添加图例 程序得到结果如图 3-6 所示。由图可见,原始数据中频率 3000Hz~3500Hz 之间,相位变 化了 3.5873(单位:弧度),大于默认的阈值,因此 unwrap()函数通过把相位减去 2π从而把 相位跳变校正为 2.6959。 函数 cplxpair()把复数数组排列成复数的共轭对。函数 cplxpair()的基本调用格式如下。 • B = cplxpair(A),以默认的误差容限 100*eps 寻找复数的共轭对; • B = cplxpair(A,tol),以指定的误差容限 tol 寻找复数的共轭对; • B = cplxpair(A,[],dim),在 dim 维度上,以默认的误差容限寻找复数的共轭对; • B = cplxpair(A,tol,dim),在 dim 维度上,以指定的误差容限 tol 寻找复数的共轭对。 如果输入矩阵的复数个数是奇数或者在误差容限内复数不能组合成共轭对,MATLAB 7.0 将返回如下错误信息: Complex numbers can’t be paired. –82– 第 3 章 数学运算 图 3-6 unwrap()函数 例如,把复数数组排列成复数的共轭对,具体代码如下: A=[1+i,2,0.5-3i,5+4i,6,1-i,5-4i,0.5+3i]; %复数矩阵 cplxpair(A) %排列成共轭对 由上述语句输出如下代码: ans = Columns 1 through 4 0.5000 - 3.0000i 0.5000 + 3.0000i 1.0000 - 1.0000i 1.0000 + 1.0000i Columns 5 through 8 5.0000 - 4.0000i 5.0000 + 4.0000i 2.0000 6.0000 3.2.4 截断和求余函数 MATLAB 7.0 提供的截断和求余函数及其功能如表 3-9 所示。 表 3-9 截断和求余函数 函数名 功能描述 函数名 功能描述 fix 向零取整 mod 除法求余(与除数同号) floor 向负无穷方向取整 rem 除法求余(与被除数同号) ceil 向正无穷方向取整 sign 符号函数 round 四舍五入 采用下面的例子来说明函数 fix()、floor()、ceil()和 round()的区别,具体代码如下: a=[-1.55 -1.45 1.45 1.55]; a_fix=fix(a) a_floor=floor(a) a_ceil=ceil(a) a_round=round(a) –83– MATLAB 7.0 从入门到精通 由上述语句得到如下代码: a_fix = -1 -1 1 1 a_floor = -2 -2 1 1 a_ceil = -1 -1 2 2 a_round = -2 -1 1 2 用下面的例子说明函数 mod()和 rem()的区别,具体代码如下: a=[10 -10 10 -10]; b=[8 -8 -8 8]; c_rem=rem(a,b) c_mod=mod(a,b) 由上述语句得到如下代码: c_rem = 2 -2 2 -2 c_mod = 2 -2 -6 6 求矩阵的符号使用函数 sign(),示例代码如下: a=-2:2; a_sign=sign(a) 由上述语句得到如下代码: a_sign = -1 -1 0 1 1 3.3 特殊数学函数 本小节介绍一些用途比较特殊的数学函数,包括特殊函数、数论函数和坐标变换函数。 3.3.1 特殊函数 特殊函数通常是数学物理方程的解,并且经常在数学、物理和工程问题中出现。MATLAB 7.0 提供的特殊函数及其功能如表 3-10 所示。 表 3-10 函数名 airy besselj bessely 功能描述 Airy 函数 第一类 Bessel 函数 第二类 Bessel 函数 特殊函数 函数名 erfc erfcx erfinv 功能描述 余误差函数:erfc(x)=1-erf(x) erfcx(x) = exp(x^2) * erfc(x) 误差函数的逆函数 –84– 第 3 章 数学运算 续表 函数名 besselh besseli besselk beta betainc betaln ellipj ellipke erf 功能描述 第三类 Bessel 函数 第一类 Bessel 函数 第二类改进的 Bessel 函数 Beta 函数 不完全 Beta 函数 对数 Beta 函数 Jacobi 椭圆函数 完全椭圆积分 误差函数 函数名 expint gamma gammainc gammaln psi legendre cross dot 功能描述 指数积分函数 Gamma 函数 不完全 Gamma 函数 对数 Gamma 函数 多 (Polygamma)函数 连带勒让德函数 矢量叉乘 矢量点乘 在后面的章节将详细介绍这些特殊函数的定义和用法。为了阅读方便,把这些特殊函数 进行分类,包括:Airy 函数、Bessel 函数、Gamma 函数、Beta 函数、Jacobi 椭圆函数和完全 椭圆积分、误差函数、指数积分函数和连带勒让德函数。 1.Airy 函数 Airy 函数是微分方程 d2W dZ 2 − ZW = 0 的解。有两类 Airy 函数:第一类 Airy 函数 Ai(Z) 和第二类 Airy 函数 Bi(Z)。它们可以用改进的第一类 Bessel 函数 Iv(Z)和改进的第二类 Bessel 函数 Kv(Z)来定义,表达式如下: Ai(Z ) = 1 π Z 3    I1 3 (2 3 Z 3 2 ), Bi(Z ) = Z 3    K − 1 3 ( 2 3 Z 3 2 ) + K 1 3 ( 2 3 3 Z 2 )  函数 Airy ()的调用方式如下: • W = Airy(Z),返回第一类 Airy 函数 Ai(Z); • W = Airy(0,Z),与 Airy (Z)相同; • W = Airy(1,Z),返回第一类 Airy 函数 Ai(Z)的导数 Ai’(Z); • W = Airy (2,Z),返回第二类 Airy 函数 Bi(Z); • W = Airy (3,Z),返回第二类 Airy 函数 Bi(Z)的导数 Bi’(Z)。 2.Bessel 函数 Bessel 函数是微分方程 Z2 d2 y dz 2 + Z dy dZ + (Z 2 − v2)y = 0 的解,其中 是常量。该方程有 两个线性无关的解,第一类 Bessel 函数 Jv (Z ) 和第二类 Bessel 函数 Yv (Z ) ,它们的表达式 如下: –85– MATLAB 7.0 从入门到精通 ∑ Jv (Z ) =   Z 2 v  ∞ k =0 (Z )k k !Γ(v + k + 1) Yv (Z) = Jv (Z ) cos(vπ) − sin(vπ) J−v (Z ) 有时也采用 Hankel 函数来表示 Bessel 方程,它们是第一类 Bessel 函数和第二类 Bessel 函数的线性组合: H(1) v (Z ) = Jv ( Z ) + iYv ( Z ) , H(1) v (Z ) = Jv ( Z ) − iYv ( Z ) 。 Hankel 函数 H (k v ) 也称为第三类 Bessel 函数。 在 MATLAB 7.0 中与 Bessel 函数相关的函数的调用方式如下。 • J = besselj(nu,Z),返回第一类 Bessel 函数 Jv (Z ) ; • J = besselj(nu,Z,1),besselj(nu,Z).*exp(-abs(imag(Z))); • Y = bessely(nu,Z),返回第二类 Bessel 函数 Yv (Z ) ; • Y = bessely(nu,Z,1),返回 bessely(nu,Z).*exp(-abs(imag(Z))); • H = besselh(nu,K,Z),返回第三类 Bessel 函数 H(vk ) ; • H = besselh(nu,Z),返回 H(1) v 。 改进的 Bessel 函数是微分方程 Z 2 d2 y dZ 2 + Z dy dZ − (Z 2 + v2 ) y = 0 的解,其中 是常量。 该方程有两个线性无关的解:第一类改进的 Bessel 函数 Iv (Z ) 和第二类改进的 Bessel 函数 Kv (Z ) ,它们的表达式如下: ∑ Iv (Z ) =   Z 2 v  ∞ k =0 k ( Z 2 )k 4 !Γ(v + k + 1) Kv (Z ) = πI−v (Z ) − Iv (Z ) 2 sin(vπ) 在 MATLAB 7.0 中与改进的 Bessel 函数相关的函数的调用方式如下。 –86– • I = besseli(nu,Z),返回第一类改 2 进的 Bessel 函数 Iv (Z ) ; • I = besseli(nu,Z,1),返回 besseli(nu,Z).*exp(-abs(real(Z))); • K = besselk(nu,Z),返回第二类改进的 Bessel 函数 Kv (Z ) ; • K = besselk(nu,Z,1),返回 besselk(nu,Z).*exp(Z)。 3.Gamma 函数和 Beta 函数 在 MATLAB 7.0 中,Gamma 函数和 Beta 函数的定义如下。 ∫ • Gamma 函数: Γ(a) = ∞ e−tt a−1dt ; 0 ∫ • 不完全 Gamma 函数: P(x, a) = 1 x e−tt a−1dt ; Γ(a) 0 第 3 章 数学运算 •多 函数:ψ n (x) = d n−1ψ (x) dxn−1 ,其中ψ(x)= Γ′(x) = Γ(x) d ln(Γ(x)) dx ,ψ n ( x) 称为(n+1) 函数,例如ψ 3( x) 称为 4 函数; ∫ • Beta 函数: B(z, w) = 1t z−1(1− t)w−1dt = Γ(z)Γ(w) ; 0 Γ(z + w) ∫ • 不完全 Beta 函数: Ix (z, w) = 1 B(z, w) x t z−1(1 − t)w−1dt 。 0 在 MATLAB 7.0 中与 Gamma 函数和 Beta 函数相关的函数的调用方式如下。 • Y = gamma(a),返回 Gamma 函数 Γ(a) ; • Y = gammainc(x,a),返回不完全 Gamma 函数 P(x, a) ; • Y = gammainc(X,A,tail),当 tail=’lower’时返回 P(x, a) ,当 tail=’upper’时返回 1- P(x, a) ; • Y = gammaln(A),返回 ln(Γ(a)) ,可以避免采用 log(gamma(a))造成的溢出情况; • Y = psi(X),返回双 函数ψ1( x) ; • Y = psi(k,X),返回 k+2 函数ψ k+1( x) ; –87– MATLAB 7.0 从入门到精通 • B = beta(z,w),返回 Beta 函数 B(z, w) ; • I = betainc(x,z,w),返回不完全 Beta 函数 Ix (z, w) ; • L = betaln(z,w),返回 ln(B(z, w)) ,可以避免采用 log(beta(a))造成的溢出情况。 4.Jacobi 椭圆函数和完全椭圆积分 Jacobi 椭圆函数 sn(u)、cn(u)和 dn(u)是定义在勒让德第一类椭圆积分基础上的。其中, 勒让德第一类椭圆积分表达式如下: u(m,φ) = φ ∫0 dθ 1 ( ) 1− m sin2 (θ ) 2 椭圆积分的反函数定义为 ƒ = am(u) ,则 sn(u)、cn(u)和 dn(u)的表达式为: sn(u) = sin(φ) cn(u) = cos(φ) 1 dn(u) = (1− m sin2 φ)2 第一类完全椭圆积分 K(m)也是定义在勒让德第一类椭圆积分基础上的,其表达式如下: K(m) = u(m, π) = ∫ ( ) 2 π 2 dθ 0 1− m sin2 (θ ) 1 2 第二类完全椭圆积分 E(m)的定义如下: π 1 ( ) ∫ E(m) = 2 1− msin2 (θ ) 2 dθ 0 在 MATLAB 7.0 中与 Jacobi 椭圆函数和完全椭圆积分相关的函数的调用方式如下。 • [SN,CN,DN] = ellipj(U,M),返回 Jacobi 椭圆函数 sn(u),cn(u)和 dn(u); • [SN,CN,DN] = ellipj(U,M,tol),以指定精度 tol 计算 Jacobi 椭圆函数,默认精度是 eps, 若指定一个更大的值会降低计算精度,提高计算速度; • K = ellipke(M),返回第一类完全椭圆积分 K(m); • [K,E] = ellipke(M),返回第一类完全椭圆积分 K(m)和第二类完全椭圆积分 E(m); • [K,E] = ellipke(M,tol),以精度指定 tol 计算完全椭圆积分,若指定一个更大的值会降低 计算精度,提高计算速度。 5.误差函数 与误差函数有关的表达式如下: –88– ∫ • 误差函数: erf (x) = 2 x e−t2 dt π0 第 3 章 数学运算 ∫ • 余误差函数: erfc(x) = 2 ∞ e−t2 dt = 1− erf (x) πx 在 MATLAB 7.0 中与误差函数相关的函数的调用方式如下。 • Y = erf(X),返回误差函数; • Y = erfc(X),返回余误差函数; • Y = erfcx(X),返回 ex2erfc(x) ; • X = erfinv(Y),返回误差函数的反函数; • X = erfcinv(Y),返回余误差函数的反函数。 6.指数积分函数 ∫ 指数积分表达式为: E1(x) = ∞ e−t dt xt 在 MATLAB 7.0 中用函数 expint()计算指数积分,其调用格式如下。 Y = expint(X),返回指数积分 E1(x)。 7.连带勒让德函数 连带勒让德函数是连带勒让德方程 (1 − x2 ) d2 y dx2 − 2x dy dx +    n(n + 1) − m2 1− x2    y = 0 的 解,记为 Pnm (x) 。连带勒让德函数可以用勒让德多项式来表示,表达式如下: Pnm (x) = (−1)m (1− m x2) 2 dm dxm Pn (x) 其中 Pn(x)为勒让德多项式,其表达式如下: Pn (x) = 1  dn 2n n!  dxn (x2 −1)n   有时需要对连带勒让德函数进行归一化,有施密特半归一化和完全归一化两种方法。施 密特半归一化后的连带勒让德函数记为 Smn (x) ,它与 Pnm (x) 的关系如下: S0n (x) = Pn0 (x) –89– MATLAB 7.0 从入门到精通 Smn (x) = (−1)m 2((nn+−mm))!!Pnm (x)(m > 0) 完全归一化方法的目的是为了使归一化后的连带勒让德函数 N m n ( x) 满足下式: ∫ ( ) 1 −1 N m n ( x) 2 dx = 1 完全归一化后的连带勒让德函数 N m n ( x) 与 Pnm ( x) 的关系如下: N m n ( x) = (−1)m   n +1 2 (n   (n − + m)! m) ! Pnm ( x) 在 MATLAB 7.0 中函数 legendre ()用于连带勒让德函数,其调用格式如下。 • P = legendre(n,X),返回连带勒让德函数 Pnm (x) ,m=0,1,…,n,n 必须是标量整数,x 必 须在[-1,1]范围内的实数。如果 X 是 1×q 的向量,则 P 是(n+1)×q 的矩阵,矩阵元素 P(m+1,i)代表 Pnm (x(i)) 。一般来说,矩阵 P 总是比矩阵 X 多一个维度; • S = legendre(n,X,’sch’),返回施密特半归一化后的连带勒让德函数 Smn (x) ; • N = legendre(n,X,’norm’),返回完全归一化后的连带勒让德函数 Nmn (x) 。 3.3.2 数论函数 MATLAB 7.0 提供的数论函数及其功能如表 3-11 所示。 表 3-11 函数名 factor isprime primes 功能描述 分解质因子 是否为素数 小于等于输入值的素数 数论函数 函数名 rat rats perms gcd 最大公因素 nchoosek lcm 最小公倍数 factorial 例如,求 78 的所有质因子,具体代码设置如下: f=factor(78) 由上述语句得到如下代码: f= 2 3 13 功能描述 把实数近似为有理数 利用 rat 函数来显示输出 给出向量的所有置换 计算 Ckn ,即 n 种事物中一次取出 k 种事物的组合数目 阶乘 –90– 例如,求 C130 ,具体代码设置如下: c=nchoosek(10,3) 由上述语句得到如下代码: c= 120 第 3 章 数学运算 3.3.3 坐标变换函数 MATLAB 7.0 提供的坐标变换函数及其功能如表 3-12 所示。 表 3-12 坐标变换函数 函数名 cart2sph cart2pol pol2cart 功能描述 笛卡尔坐标系转换为球坐标系 笛卡尔坐标系转换为极坐标系 函数名 sph2cart hsv2rgb 极坐标系转换为笛卡尔坐标系 rgb2hsv 功能描述 球坐标系转换为笛卡尔坐标系 灰度饱和度颜色空间转换为 RGB 颜色空间 RGB 颜色空间转换为灰度饱和度颜 色空间 例如,把笛卡尔坐标系中的点(1,1,1)分别转换到球坐标系和极坐标系中,代码设置如 下: [THETA,PHI,R] = cart2sph(1,1,1) [THETA,RHO,Z] = cart2pol(1,1,1) 由上述语句得到如下代码: THETA = 0.7854 PHI = 0.6155 R= 1.7321 THETA = 0.7854 RHO = 1.4142 Z= 1 –91– 第 4 章 MATLAB 7.0 基本编程 MATLAB 作为一种广泛应用于科学计算的工具软件,不仅具有强大的数值计算、符号计 算、矩阵运算的能力和丰富的画图功能,还可以像 C 语言、FORTRAN 等计算机高级语言一 样进行程序设计,编写扩展名为.m 的 M 文件,实现各种复杂的运算,这使得 MATLAB 在科 研中的应用更加深入,常常作为系统仿真的工具应用。MATLAB 提供文件编辑器和编译器, 这为用户带来了方便,事实上,MATLAB 自带的许多函数就是 M 文件函数,用户也可利用 M 文件来生成和扩充自己的函数库。 所谓 M 文件,简单来说就是用户把要实现的命令写在一个以.m 作为扩展名的文件中, 然后由 MATLAB 系统进行解释,最后运行出结果。由此可见 MATLAB 具有强大的可开发性 和可扩展性。另外,由于 MATLAB 是由 C 语言开发而成的,因此 M 文件的语法规则与 C 语 言几乎一样,简单易学。 本章将讨论 MATLAB 中编程的规则和注意事项,并给出很多典型的例程帮助读者尽快 熟悉。 4.1 脚本和函数 M 文件有函数(Functions)和脚本(Scripts)两种格式。二者相同之处在于它们都是以 m 作为扩展名的文本文件,不进入命令窗口,而是由文本编辑器来创建外部文本文件。但是 两者在语法和使用上略有区别。 4.1.1 函数 MATLAB 中许多常用的函数(如 sqrt、inv 和 abs 等)都是函数式 M 文件,使用时,MATLAB 获取传递给它的变量,利用操作系统所给的输入,运算得到要求的结果,然后返回这些结果。 函数文件类似于一个黑箱,由函数执行的命令以及这些命令所创建的中间变量都是隐含的。 运算过程中的中间变量都是局部变量(除特别声明外),存放在函数本身的工作空间内,不会 和 MATLAB 基本工作空间(Base workspace)的变量相互覆盖,对用户来说,可见的只是输 入和输出,因此易于使程序模块化,特别适合于大型程序代码。 下面通过一个函数的例子来说明其结构。 >>type function1.m function average=function1(value) average=sum(value)/length(value); 此函数的第一行为函数定义行,以 function 语句作为引导,定义了函数名称(function1, 需要注意的是函数名和文件名必须相同)、输入自变量(value,调用此函数时指定此变量的 第 4 章 MATLAB 7.0 基本编程 值,类似于 C 语言的形式参数)和输出自变量(average,函数执行完毕返回的结果)。function 为关键词,说明此 M 文件为函数。第二行则为函数主体,规范函数的运算过程,并指出输出 自变量的值。若调用此函数,可输入以下命令: >>value=[1 2 3]; >>average=function1(value) average= 2 此外,还可在函数定义行下加入注解,以%开头,即函数的在线帮助,若在 MATLAB 输入“help 函数主文件名”,即可看到这些帮助,需要注意的是,在线帮助和 M 函数定义行 之间可以有空行,但是在线帮助的各行之间不应有空行。示例代码如下: >>type function2.m function average=function2(vector) %function2 A simple function with a single help line %usage of this function %"output" is the average of the input vector "input". %Roger Jang,19991123. average=sum(vector)/length(vector); %计算平均值 >>help function2 function2 A simple function with a single help line "output" is the average of the input vector "input". 如果函数中有注释,代码设置如下: >>type function2.m function average=function2(vector) %function2 A simple function with a single help line %usage of this function %"output" is the average of the input vector "input". %Roger Jang,19991123. average=sum(vector)/length(vector); %计算平均值 则在线帮助只有第一行: >>help function2 function2 A simple function with a single help line 以上的例子只是为了说明函数的性质和用法,有关编程的方法、具体问题以及典型例子 会在后面的章节介绍。 4.1.2 脚本 脚本是一个扩展名为.m 的文件,其中包含了 MATLAB 的各种命令,与批处理文件很类 似,在 MATLAB 命令窗口下直接输入此文件的主文件名,MATLAB 可逐一执行在此文件内 的所有命令,和在命令窗口逐行输入这些命令一样。脚本式 M 文件运行产生的所有变量都是 –93– MATLAB 7.0 从入门到精通 全局变量,运行脚本后,所产生的所有变量都驻留在 MATLAB 基本工作空间内,只要用户 不使用 clear 命令加以清除,且 MATLAB 指令窗口不关闭,这些变量将一直保存。基本空间 随 MATLAB 的启动而产生,在关闭 MATLAB 软件时该基本空间被删除。 例如,假设当前目录下有一个脚本 M 文件,可用 type 命令显示其内容如下: >>type solver.m %solver.m %used to solve A*X=b %where A=[-1.5 1 2;3 -1 1;-1 3 5], b=[2.5;5;8]. A=[-1.5 1 2;3 -1 1;-1 3 5]; b=[2.5;5;8]; X=A\b 在上面的示例代码中以%开头的行是注释,在命令窗口执行 solver 命令,即可得到方程 组的解,具体代码如下: >>solver X= 0.7500 -0.6250 2.1250 结合上例,下面对 M 文件必须遵循的规则及两种类型的异同做简要介绍。 (1)从函数名必须与文件名相同。 (2)脚本式 M 文件没有输入参数或输出参数,而函数式 M 文件有输入参数和输出参数。 (3)函数可以有零个或多个输入和输出变量。函数 nargin 和 nargout 包含输入和输出变 量的个数。在运行时,可以按少于 M 文件中规定的输入和输出变量的个数进行函数调用,但 不能多于这个标称值。 从运行上看,与脚本文件不同的是,函数文件被调用时,MATLAB 会专门为它开辟一个 临时工作空间,称为函数工作空间(Function workspace),用来存放中间变量,当执行完函 数文件的最后一条命令或者遇到 return 时就结束该函数文件的运行,同时该临时函数空间及 其所有的中间变量将被清除。函数工作空间相对于基本空间是临时的、独立的,在 MATLAB 运行期间,可以产生任意多个临时函数空间。 (4)在 M 文件中,包括脚本和函数,到第一个非注释行为止的注释行是帮助文本,当需 要帮助时,返回该文本,通常用来说明文件的功能和用法。 (5)函数 M 文件中的所有变量除特殊声明外都是局部变量,而脚本中的变量都是全局 变量。 (6)变量的命名可以包括字母、数字和下划线,但必须是以字母开头。并且在 M 文件设 计中是区分大小写的。变量的长度不能超过系统函数 namelengthmax 所规定的值。 (7)假设在函数文件中发生对某脚本文件的调用,那么该脚本文件运行产生的所有变量 都存放于此函数空间中,而不是存在基本工作空间中。 通常 M 文件是文本文件,所以可使用一般的文本编辑器编辑 M 文件,存储时以文本模 式存储。此外,MATLAB 内部自带了 M 文件编辑器与编译器,可选择 MATLAB 命令窗口上 的 file/new 子菜单,然后选择下一级子菜单中的 M-file 子菜单,就进入了 M 文件编辑/编译器, –94– 第 4 章 MATLAB 7.0 基本编程 如图 4-1 所示。它是一个集编辑与调试两种功能于一体的工具环境。进行代码编辑时,它可 以用不同的颜色来显示注解、关键词、字符串和一般程序代码,使用非常方便。在书写完 M 文件后,也可以像一般的程序设计语言一样,对 M 文件进行调试、运行。 图 4-1 M 文件编辑器和编译器 4.1.3 子函数与私有目录 一个 M 文件可以包含一个以上的函数,其中有一个主函数,其他为子函数。这些子函数 只能被同一文件中的函数(主函数或其他子函数)调用,但是不能被其他文件的函数调用。 在一个 M 文件中,主函数必须出现在最上方,其后可接上任意数目的子函数,而且子函数的 次序可随意。同一文件的主函数、子函数的工作空间都是彼此独立的,各函数间的信息可通 过输入输出宗量、全局变量或跨空间指令传递。 此外,可以在某一目录中建立一个自己命名的私有目录来存放相关的函数,例如在 work 目录下建立一个 rscode 目录,则 work 中的 M 文件(无论是脚本还是函数)即可调用 rscode 下的任何函数,而不必再定义其他搜寻路径。在 rscode 下的函数,只能被其父目录的函数所 调用而不能被其他目录下的函数调用。 当 M 文件中需要调用某一个函数时,MATLAB 是按照以下顺序来搜寻的: • 检查此函数是否是子函数; • 检查此函数是否为私有目录的函数; • 从所设定的搜寻路径搜索此函数。 搜索过程中,只要找到与第一个文件名相符的函数就会立即取用而停止搜索。 4.1.4 P 码文件 P 码是伪代码 Psedocode 的简写,一个 M 文件首次被调用时,MATLAB 将首先对该 M 文件进行语法分析,并把生成的相应内部伪代码(P 码)存放在内存中。此后当再次调用该 M 文件时,将直接运行该文件在内存中的 P 码文件而不会对原码文件重复进行语法分析。P 码文件和原码文件具有相同的文件名,但其扩展名为“.p”。并且其运行速度要高于原码文件, 但是对于规模不大的文件,用户一般察觉不到这种速度上的优势。 –95– MATLAB 7.0 从入门到精通 尤其在 MATLAB 环境中,假如存在同名的 P 码和原码文件,则当该文件名被调用时, 被执行的肯定是 P 码文件。 P 码文件不是只有当 M 文件被调用时才可产生,也可被预先生成。这种功能可以用作代 码保护的手段,生成 P 码文件后,其他用户可以使用该代码,但是无法看到代码的内容。具 体操作如下: pcode filename %在当前目录下生成 filename.p pcode filename – inplace %在 filename.m 所在目录下生成 filename.p 如果要在内存中对 P 码文件进行操作,可键入以下命令: inmem %罗列出内存中的所有 P 码文件 clear filename %清除内存中的 filename.pP 码文件 clear functions %清除内存中的所有 P 码文件 4.2 MATLAB 中的变量和语句 MATLAB 能识别一般常用的加、减、乘、除和幂等运算,对于简单的计算可以在命令窗 口中输入表达式后,按 Enter 键即可完成。MATLAB 会将运算结果存入默认变量 ans 中,并 显示其结果(如果表达式后加上分号“;”,则不会显示),当然用户也可设定自己的变量。与 C 语言不同的是,MATLAB 中的变量是不需要事先定义的。 MATLAB 的主要功能虽然是数值运算,但是它也是一个完整的程序语言,有各种语句格 式和语法规则。下面就进行详细介绍。 4.2.1 变量类型 与 C 语言的区别是,在用 M 语言编写程序的过程中,变量不需要事先定义。但 MATLAB 中的变量也有自己的命名规则,即必须以字母开头,之后可以是任意字母、数字或下划线, 不能有空格;变量名区分大小写;在 MATLAB 7.0 中,变量名不能超过 63 个字符,第 63 个 字符之后的部分将被忽略。 除了上述命名规则外,MATLAB 还包括一些特殊的变量,如表 4-1 所示。 表 4-1 MATLAB 中的特殊变量 变量名称 ans pi eps inf NaN i(j) nargin nargout realmin realmax MATLAB 中默认变量 圆周率 计算机中的最小数 无穷大 不定值,如 0/0 复数中的虚数单位 所用函数的输入变量数目 所用函数的输出变量数目 最小可用正实数 最大可用正实数 变量含义 用户定义的变量有局部变量和全局变量两种类型。每一个函数在运行时,均占用单独的 –96– 第 4 章 MATLAB 7.0 基本编程 一块内存,此工作空间独立于 MATLAB 的基本工作空间和其他函数的工作空间,因此不同 工作空间的变量完全独立不会相互影响,这些变量称为局部变量。有时为了减少变量的传递, 可使用全局变量,它是通过 global 指令定义的,格式为: global var1 var2; 通过上述指令,可以使 MATLAB 允许几个不同的函数空间以及基本工作空间共享同一 个变量。每个希望共享全局变量的函数或 MATLAB 基本工作空间必须逐个对具体变量加以 专门定义,没有采用 global 定义的函数或基本空间将无权使用全局变量。 如果某个函数的运行使得全局变量发生了变化,则其他函数空间及基本工作空间内的同 名变量随之变化。只要与全局变量相联系的工作空间有一个存在,则全局变量存在。 在使用全局变量中需要注意以下几个方面。 • 在使用之前必须首先定义,建议将定义放在函数体的首行位置。 • 虽然对全局变量的名称并没有特别的限制,但是为了提高程序的可读性,建议采用大 写字符命名全局变量。 • 全局变量会损坏函数的独立性,使程序的书写和维护变得困难,尤其是在大型程序中, 不利于模块化,这里不推荐使用。 例如,在函数 sum2(y)中声明了一个全局变量,内容如下: function z=sum2(y) global X z=X+y; 要测试此函数,可在命令窗口进行如下操作: >>global X %在基本工作空间进行全局变量 X 的声明 >>X=3 >> z=sum2(2) z= 5 >> whos global %查看工作空间的全局变量 Name Size Bytes Class X 1x1 8 double array (global) Grand total is 1 element using 8 bytes clear global X %清除全局变量 X 除命名规则外,变量命名时还需要注意以下两个方面: (1)不要把函数名用作变量,否则在没有从内存中清除该变量的情况下将不能调用该函 数,要测试变量名是否已用作函数名,可键入 which –all进行确认; (2)MATLAB 预留了一些关键字并且不允许重载它们,定义变量时要避开这些关键字, (用 iskeyword 可列出所有的预留关键字)否则系统会显示类似于缺少操作数之类的错误信息。 4.2.2 M 文件的流控制语句 一般来讲,决定程序结构的语句可分为顺序语句、循环语句和分支语句 3 种,每种语句 有各自的流控制机制,相互配合使用可以实现功能强大的程序。 –97– MATLAB 7.0 从入门到精通 1.顺序语句 顺序语句就是依次顺序执行程序的各条语句,批处理文件就是典型的顺序语句的文件, 这种语句不需要任何特殊的流控制。示例代码如下: %定义变量 a a=[15,20,25,30,35]; %定义变量 b b=[1554.88,1555.24,1555.76,1556.20,1556.68]; figure(1) %使用缺省设置进行作图,以 a 为横轴,b 为纵轴 plot(a,b) hold on %用红色标志’x’画出相关的点 plot(a,b,’rx’); 2.循环语句 循环语句一般用于有规律的重复计算。被重复执行的语句称为循环体,控制循环语句走 向的语句称为循环条件。MATLAB 中有 for 循环和 while 循环两种语句。 (1)for 循环 for 循环的语法结构如下: for 循环变量=数组 循环体; end 可以看出,这种语句与其他语言中的 for 循环结构是相同的,循环体的执行次数是确定 的,它是由数组的列数决定。此外,for 循环是可以多重嵌套的,请看下面的示例代码: %根据中心极限定理,从双极性分布的序列中获得满足正态分布的随机序列 clear; n=50; p=0.5; %生成一个满足双极性分布的矩阵,大小为 500*1000 x=binornd(n,p,500,1000); z=(x-25)/sqrt(12.5); for i=1:1:1000 y(1,i)=0; end for i=1:1:1000 for j=1:1:500 %将独立分布的双极性随机数相加 y(1,i)=y(1,i)+z(j,i); end –98– %对最终的数据进行调整 y(1,i)=y(1,i)/sqrt(500); end 第 4 章 MATLAB 7.0 基本编程 注意:不能在 for 循环体内重新对循环变量赋值来终止循环的执行,有专门的命令 break 可以完成 这一功能,后面会具体介绍。为得到高效代码,应尽可能提高代码的向量化程度,采用矩 阵运算,而避免使用循环结构,如果使用的话,在循环指令之前尽量对数组进行预定义。 (2)while 循环 while 循环的语法结构如下: while 表达式 循环体; end while 循环的次数是不固定的,只要表达式的值为真,循环体就会被执行。通常表达式给 出的是一个标量值,但也可以是数组或者矩阵,如果是后者,则要求所有的元素都必须为真。 示例代码如下: t=zeros(1,6); i=1;5 while i<=6 t(1)=1/i; i=i+1; end 3.条件语句 在程序中如果需要根据一定条件来执行不同的操作时就需要用到条件语句了,MATLAB 中有 if-else-end 语句和 switch-case-otherwise 语句两种条件语句。 (1)if-else-end 语句 其语法结构如下: if 条件式 表达式 1; else 表达式 2; end 也可有更简化的结构: if 条件式 表达式; end 有多个条件式的复杂结构: if 条件式 1 表达式 1; –99– MATLAB 7.0 从入门到精通 elseif 条件式 2 表达式 2; elseif 条件式 3 表达式 3; …… else 表达式 n; end 如果在条件式中使用矩阵,则必须矩阵元素都不为 0 时,条件式才算成立。 下面是一个简单的分支语句的例程,代码设置如下: function y=control(n) a=20; if n==0 y=a+1; elseif n==1 y=a*(1+n); elseif n==2 y=a+n; else y=a; end (2)switch-case-otherwise 语句 此语句与 C 语言中的选择语句是相同功能的,它通常用于条件较多而且较单一的情况, 类似于一个数控的多路开关。其语法结构如下: switch expression case value1 statements1; case value2 statements2; …… case valuen statementsn; otherwise statements; end expression 是一个标量或者字符串,将 expression 的值依次和各个 case 指令后面的检测 值进行比较,当比较结果为真时,MATLAB 执行后面的一组命令,然后跳出该 switch 结构。 如果所有的比较结果都为假,则执行 otherwise 后的命令。当然 otherwise 指令也可以不存在。 下面是 MATLAB 工具箱中的 itemsmsg 函数的示例,其中运用了 switch 结构,代码设置 –100– 如下: function str = itemsmsg(type, items) str = ’’; switch type case ’Error’ % Display each item’s error message errItem = {items.ItemID}; errMsg = {items.ErrorMessage}; allStrs = [errItem; errMsg]; str = sprintf(’\t\t%s returned: ’’%s’’\n’, allStrs{:}); case ’ReadAsync’ str = sprintf(’\t%d items read.’, length(items)); case ’WriteAsync’ str = sprintf(’\t%d items written.’, length(items)); end 第 4 章 MATLAB 7.0 基本编程 4.其他流控制语句 在许多程序设计中会碰到需要提前终止循环、跳出子程序、显示出错信息等情况,因此 还需要其他的流控制语句来实现这些功能。主要有 continue、break、return、echo、error、try… catch 等。下面对各语句分别进行介绍。 (1)continue 此命令的作用是结束本次循环,即跳过循环体中尚未执行的语句,接着进行下一次是否 执行循环的判断。以下是 MATLAB 自带的 M 文件 magic.m 中运用 continue 的示例,代码设 置如下: fid=fopen(’magic.m’,’r’); count=0; while ‘feof(fid) line=fgetl(fid); if isempty(line)|strncmp(line,’%’,1) continue end count=count+1; end disp(sprintf(’%d lines’,count)); (2)break 此命令的作用是终止本次循环,跳出最内层循环,即不必等到循环的结束而是根据条件 退出循环,它的用法和 continue 类似,常常和 if 语句合用来强制终止循环。示例代码如下: fid=fopen(’ fft.m’, ’r’); s="; –101– MATLAB 7.0 从入门到精通 while ‘feof(fid) line=fgetl(fid); if isempty(line) break end s=strvcat(s,line); end disp(s); 需要注意的是,当 break 命令碰到空行时,将退出 while 循环。 (3)return 此命令可使正在运行的函数正常退出,并返回调用它的函数继续运行,经常用于函数的 末尾以正常结束函数的运行,当然也可用在某条件满足时强行结束执行该函数。例如在 showopcevents.m 函数中,就运用了这一控制命令。 if isempty(eventStruct), out = []; return; end (4)echo 通常执行 M 文件时,在命令窗口是看不到执行过程的,但在特殊情况下比如需要作演示, 要求 M 文件的每条命令都要显示出来,可以用 echo 命令实现这样的操作。 对于脚本式 M 文件和函数式 M 文件,echo 命令有所不同,对于脚本式 M 文件,echo 命令可以用以下方式来实现: echo on %显示其后所有执行的命令文件的指令 echo off %不显示其后所有执行的命令文件的指令 echo %在上述两种情况之间切换 对于函数式 M 文件,echo 命令可以用以下方式来实现: echo filename on %使 filename 指定的 M 文件的执行命令显示出来 echo filename off %使 filename 指定的 M 文件的执行命令不显示出来 echo on all %其后的所有 M 文件的执行指令显示出来 echo off all %其后的所有 M 文件的执行指令显示出来 (5)error 此指令是用来指示出错信息并终止当前函数的运行。语法格式如下: error(‘message’) 类似的还有 warning 指令,二者区别在于 warning 指示警告信息后程序仍继续运行。 (6)try…catch 其功能与 error 类似,用于对异常情况进行处理,其语法结构如下: try (commands1) catch –102– 第 4 章 MATLAB 7.0 基本编程 (commands2) end 组命令 1 总会被执行,当执行出现错误时,catch 控制块就可捕获它,执行组命令 2,针 对不同的错误类型进行不同的处理。可调用 lasterr 函数查询出错原因。示例代码如下: clear; n=4; a=magic(3); try a_n=a(n,:), catch a_end=a(end,:), end lasterr 运行结果如下: a_end = 492 ans = Attempted to access a(4,:); index out of bounds because size(a)=[3,3]. 注意 try 和 catch 控制块中的语句之间用逗号隔开。 (7)input 此命令用来提示用户从键盘输入数据、字符串或表达式,并接收输入值。语法格式如下: user_entry=input(’prompt’) %在屏幕上显示提示信息 prompt,等待用户的输入,并将输入赋给变量 user_entry user_entry=input(’prompt’,’s’) %返回的字符串作为文本变量而不是作为变量名或者数值 下面是一个等待用户确认显示的 M 文件的示例,代码设置如下: function test() r=input(’Do you want more?Y/N[Y]:’,’s’); if isempty(r) r=’Y’; end if r==’Y’ disp(’you have selected the first character’); else disp(’you have selected the second one’); end 用户运行这个文件时,命令窗口将出现以下信息: >>test Do you want more?Y/N[Y]: %按回车键表示确认 –103– MATLAB 7.0 从入门到精通 you have selected the first character >>test Do you want more?Y/N[Y]: n %按 n 键,然后按回车键 you have selected the second one (8)keyboard 此命令被放置在 M 文件中,将停止文件的执行并将控制权交给键盘。通过在提示符前显 示 K 来表示一种特殊状态。在 M 文件中使用该命令,对程序的调试和在程序运行中修改变 量都很方便。 如果在上例中某个位置加入 keyboard 命令,则执行到这句话时,MATLAB 的命令窗口 将显示如下代码: >> test K>> (9)pause 此命令用于暂时中止程序的运行,等待用户按任意键继续进行。该命令在程序的调试过 程和用户需要查询中间结果时使用很方便。该命令的语法格式如下: pause %停止 M 文件的执行,按任意键继续 pause(n) %中止执行程序 n 秒后继续,n 是任意实数 pause on %允许后续的 pause 命令中止程序的运行 pause off %禁止后续的 pause 命令中止程序的运行 4.3 程序的调试(Debug) 对于编程者来说,程序运行时出现 bug 在所难免,尤其是在大规模、多人共同参与的情 况下,因此掌握程序调试的方法和技巧对提高工作效率很重要。一般来说,错误可分为两种, 即语法错误(Syntax Errors)和逻辑错误(Logic Errors)。语法错误一般是指变量名与函数名 的误写、标点符号的缺漏和 end 的漏写等,对于这类错误,MATLAB 在运行或 P 码编译时一 般都能发现,终止执行并报错,用户很容易发现并改正。而逻辑错误可能是程序本身的算法 问题,也可能是用户对 MATLAB 的指令使用不当,导致最终获得的结果与预期值偏离,这 种错误发生在运行过程中,影响因素比较多,而这时函数的工作空间已被删除,调试起来比 较困难。 下面针对上述的两种错误推荐两种调试方法,即直接调试法和工具调试法。 4.3.1 直接调试法 MATLAB 本身的运算能力强,指令系统比较简单,因此程序一般都显得比较简洁,对于 简单的程序采用直接调试法往往还是很有效的。通常采取的措施如下。 (1)通过分析后,将重点怀疑语句后的分号删掉,将结果显示出来,然后与预期值进行 –104– 第 4 章 MATLAB 7.0 基本编程 比较。 (2)单独调试一个函数时,将第一行的函数声明注释掉,并定义输入变量的值,然后以 脚本方式执行此 M 文件,这样就可保存下原来的中间变量了,可以对这些结果进行分析,找 出错误。 (3)可以在适当的位置添加输出变量值的语句。 (4)在程序中的适当位置添加 keyboard 指令。当 MATLAB 执行至此处时将暂停,并显 示 k>>提示符,用户可以查看或改变各个工作空间中存放的变量,在提示符后键入 return 指 令可以继续执行原文件。 但是对于文件规模大,相互调用关系复杂的程序,直接调试是很困难的,这时可以借助 于 MATLAB 的专门工具调试器(Debugger)进行,即工具调试法。 4.3.2 工具调试法 MATLAB 自身包括调试程序的工具,利用这些工具可以提高编程的效率,包括一些命令 行形式的调试函数和图形界面形式的菜单命令。实际工作中,可以根据个人需要进行操作。 本节主要介绍一些基本方法,在这些方法的基础上还需要读者不断实践,总结经验,才能做 到熟练运用,高效编程。 1.以命令行为主的程序调试 以命令行为主的程序调试手段具有通用性,可以适用于各种不同的平台,它主要是应用 MATLAB 提供的调试命令。在命令窗口输入 help debug 可以看到一个对于这些命令的简单描 述,下面分别进行介绍。 (1)设置断点 这是其中一个最重要的部分,可以利用它来指定程序代码的断点,使得 MATLAB 可在 断点前停止执行,从而可以检查各个局部变量的值。函数格式有以下几种: • dbstop in mfile 在文件名为 mfile 的 M 文件的第一个可执行语句前设置断点,执行该命令后,当程序运 行到 mfile 的第一个可执行语句时,可暂时中止 M 文件的执行,并进入 MATLAB 的调试模 式。M 文件必须处在 MATLAB 搜索路径或当前目录内。如果用户已经激活了图形调试模式, 则 MATLAB 调试器将打开该 M 文件,并在第一个可执行语句前设置断点。 • dbstop in mfile at lineno 在文件名为 mfile 的 M 文件的第 lineno 行设置断点,执行过程与上一命令类似。如果行 号为 lineno 的语句为非执行语句,则停止执行的同时,在该行号的下一个可执行语句前设置 断点。M 文件必须处在 MATLAB 搜索路径或当前目录内。此时,用户可以使用各种调试工 具、查看工作空间变量、公布任何有效的 MATLAB 函数。 • dbstop in mfile at subfun 执行该命令后,当程序执行到子程序 subfun 时,暂时中止文件的执行并使 MATLAB 处 于调试模式,其他要求和操作与上面的函数类似。 • dbstop if error –105– MATLAB 7.0 从入门到精通 执行该命令后,可在运行 M 文件遇到错误时,终止 M 文件的执行,并使 MATLAB 处于 调试状态,运行停止在产生错误的行。这里的错误不包括 try…catch 语句中检测到的错误, 用户不能在错误后重新开始程序的运行。 • dbstop if all error 与上一命令类似,但是在执行该命令时遇到任何类型的运行错误时均停止,包括在 try… catch 语句中检测到的错误。 • dbstop if warning 执行该命令后,在运行 M 文件遇到警告时,终止 M 文件的执行,并使 MATLAB 处于调 试状态,运行将在产生警告的行暂停,程序可以恢复运行。 • dbstop if caught error 执行该命令后,当 try…catch 检测到运行时间错误时,停止 M 文件的执行,用户可以恢 复程序的运行。 • dbstop if naninf 或 dbstop if infnan 执行该命令后,当遇到无穷值或者非数值时,终止 M 文件的执行。 下面给出一个在命令窗口设置断点的示例。 运行的 M 文件内容如下: function y=test1(x) l=length(x); y=(1:l)+x; 在命令窗口输入以下内容: >> dbstop in test1.m >> test1(1:10) 在打开的 M 文件窗口中设置断点的情况如图 4-2 所示,在第一行设置了一个断点。 图 4-2 断点图示 test1 函数中输入只能为向量,如果输入矩阵,会产生错误。因此可以根据错误进入 MATLAB 调试状态,在命令窗口输入如下: >> dbstop if error >> test1(magic(3)) –106– ??? Error using ==> unknown Matrix dimensions must agree. Error in ==> test1 at 3 y=(1:l)+x; K>> 此时,M 文件执行到最后一行,如图 4-3 所示。 第 4 章 MATLAB 7.0 基本编程 图 4-3 文件执行情况图示 由上例可以看到,程序停止执行后,MATLAB 进入调试模式,命令行上出现 K>>的提示 号,代表此时可以接受键盘输入。 (2)清除断点 • dbclear all 清除所有 M 文件中的所有断点。 • dbclear all in mfile 清除文件名为 mfile 的 M 文件中的所有断点。 • dbclear in mfile 清除 mfile 中第一个可执行语句前的断点。 • dbclear in mfile at lineno 清除 mfile 中行号为 lineno 的语句前的断点。 • dbclear in mfile at subfun 清除 mfile 中子函数 subfun 行前的断点。 • dbclear if error 清除由 dbstop if error 设置的暂停断点。 • dbclear if warning 清除由 dbstop if warning 设置的暂停断点。 • dbclear if naninf 清除由 dbstop if naninf 设置的暂停断点。 • dbclear if infnan 清除由 dbstop if infnan 设置的暂停断点。 –107– MATLAB 7.0 从入门到精通 (3)恢复执行 • dbcont 从断点处恢复程序的执行,直到遇到程序的另一个断点或错误后返回 MATLAB 基本工 作空间。 (4)调用堆栈 • dbstack 此命令显示 M 文件名和断点产生的行号,调用此 M 文件的名称和行号等,直到最高级 M 文件函数,即列出了函数调用的堆栈。 使用此命令时,有如下格式: [ST,I] = dbstack 通过 M×1 的结构体 ST 形式返回堆栈信息。ST 的形式有以下几种。 file—函数出现的文件名,如果没有则为空; name—文件中的函数名; line—函数行号。 I 是当前的工作空间的索引。 • dbstack(N) 此命令省略了显示中的前 N 个帧。 • dbstack (’-completenames’) 此命令输出堆栈中的每个函数的全名,即函数文件的名称和在堆栈中函数包含的关系。 (5)列出所有断点 • dbstatus 此命令列出所有的断点,包括错误、警告、nan 和 inf 等。 s=dbstatus 将通过一个 M×1 的结构体来返回断点信息,结构体中有以下字段。 name—函数名; line—断点行号向量; expression—与 line 中相对应的断点条件表达字符串; cond—条件字符串,如 error、caught error、warning 或 naninf; identifier—当条件字符串是 error、caught error 或 warning 时,该字段是 MATLAB 的信 息指示字符串。 • dbstatus mfile 此命令列出指定的 M 文件中的所有断点设置,mfile 必须是 M 文件函数的名称或者是 MATLAB 有效的路径名。 (6)执行 1 行或多行语句 • dbstep 执行当前 M 文件下一个可执行语句。 • dbstep nlines 执行下 nlines 行可执行语句。 • dbstep in 当执行下一个可执行语句时,如果其中包含对另外一个函数的调用,此命令将从被调用 –108– 第 4 章 MATLAB 7.0 基本编程 的函数文件的第一个可执行语句执行。 • dbstep out 此命令将执行函数剩余的部分,在离开函数时停止。 这 4 种形式的语句执行完后,都返回调试模式,如果在执行过程中遇到断点,程序将 中止。 (7)列出文件内容 • dbtype mfile 列出 mfile 文件的内容,并在每行语句前面加上标号以方便使用者设定断点。 • dbtype mfile start:end 列出 mfile 文件中指定行号范围的部分。 在 UNIX 和 VMS 调试模式下,并不显示 MATLAB 的调试器,此时必须使用 dbtype 来 显示源程序代码。 (8)切换工组空间 • dbdown 遇到断点时,将当前工作空间切换到被调用的 M 文件的空间。 • dbup 将当前工作空间(断点处)切换到调用 M 文件的工作空间。两个命令常常配合使用。 (9)退出调试模式 • dbquit 立即结束调试器并返回到基本工作空间,所有断点仍有效。 2.以图形界面为主的程序调试 MATLAB 自带的 M 文件编辑器同时也是程序的编译器,用户可以在编辑完程序后直接 进行调试,更加方便和直观。 新建一个 M 文件,即可打开编译器,选择主菜单中“Debug”选项,打开下拉菜单,有 各种调试命令,如图 4-4 所示。 图 4-4 打开的 MATLAB 调试器图形界面 –109– MATLAB 7.0 从入门到精通 下拉菜单中的命令有一部分在工具栏中有图标相对应,其功能与上一节介绍的调试命令 是相同的,下面只对各命令做简单介绍。 • step( ) 单步执行,快捷键为 F10,与调试命令中的 dbstep 相对应。 • step in( ) 深入被调函数,快捷键为 F11,与调试命令中的 dbstep in 相对应。 • step out( ) 跳出被调函数,快捷键为 Shift+F11,与调试命令中的 dbstep out 相对应。 • run/continue( ) 连续执行,快捷键为 F5,与调试命令中的 dbcont 相对应。 • go until cursor 运行到鼠标所在的行,与 dbstop in mfile at lineno 相对应。 • set/clear breakpoint( ) 设置或清除断点,快捷键为 F12,与 dbstop 和 dbclear 相对应。 • set/modify conditional breakpoint… 设置或者修改条件断点,单击此菜单项时,会弹出如图 4-5 所示的对话框,要求用户对 断点的条件作出设置,设置前光标在哪一行,则设置的断点就在这一行前。 图 4-5 set/modify conditional breakpoint 对话框 • enable/disable breakpoint 允许或者禁止断点的功用。 • clear breakpoints in all files( ) 清除所有断点,与 dbclear all 相对应。 • stop if errors/warnings 与 dbstop if error、dbstop if all error、dbstop if warning、dbstop if caught error、dbstop if naninf 和 dbstop if infnan 等命令等价,单击此菜单项时,将弹出一个对话框,如图 4-6 所示。 –110– 第 4 章 MATLAB 7.0 基本编程 图 4-6 “Stop if Errors/Warnings for All Files”对话框 可以看到,对话框的每一栏都和一个调试命令相对应,用户可以在调试前根据自己的要 求设定,然后运行程序。 • exit debug mode( ) 退出调试模式,与 dbquit 相对应。 只有当文件进入调试状态时,上述命令才会全部处于使能态。需要注意的是,在调试器 的工具栏的右侧,还有一个如图 4-7 所示的堆栈下拉菜单。在调试中,可以通过改变它的内 容来观察和操作不同工作空间中的量,类似于调试命令中的 dbdown 和 dbup。 图 4-7 工作空间切换项 下面通过一个实例来说明一下这些菜单项的用法。 【例 4.1】计算向量的标准差。 函数文件名为 strd_v.m,具体代码如下: function f=strd_v(x) l=length(x); s=sum(x); y=s/l; t=strd_fun(x,y); f=sqrt(t/(l-1)); 其中调用的子函数代码如下: function f=strd_fun(x,y) t=0; for i=1:length(x) t=t+((x-y).^2); –111– MATLAB 7.0 从入门到精通 end f=t; 在命令窗口输入如下代码: >> v=[1,2,3,4,5,6]; >> strd_v(v) 由上述语句得到如下结果: ans = 2.7386 1.6432 0.5477 0.5477 1.6432 2.7386 而 MATLAB 提供的同样功能的函数 std 计算结果如下: >> std(v) ans = 1.8708 上面的示例说明函数的逻辑有问题,需要调试,具体操作步骤如下。 在文件 strd_v.m 最后一行前设置一个断点,编译器用一个大红点标记,如图 4-8 所示, 程序运行时,将在断点处暂停。 图 4-8 设置断点 运行程序,检查变量,代码设置如下: >> strd_v(v) K>> 当运行到断点处时,在断点和文本之间将会出现一个绿色箭头,表示程序运行至此停止, 如图 4-9 所示。 –112– 第 4 章 MATLAB 7.0 基本编程 图 4-9 设置断点后程序的运行 在 K>>后检查变量 l,s,y,t 的数值,发现 t 有错误,结果如下: K>> t t= 37.5000 13.5000 1.5000 1.5000 13.5000 37.5000 检查变量也可通过变量浏览器进行,如图 4-10 所示。双击变量,即可打开数组编辑器, 可以查看和修改变量内容,如图 4-11 所示。由此可以确定断点以前的部分是正确的,出错的 是 strd_fun 子程序,需对子程序进行调试。 图 4-10 变量浏览器 –113– MATLAB 7.0 从入门到精通 图 4-11 数组编辑器 切换工作空间到基本工作空间,即将图 4-7 中的“Stack”项选为“Base”。清除前面设置 的断点,可以选择“Debug”下拉菜单中的“set/clear breakpoints”项,也可直接在大红标记 上单击一下鼠标左键。此时,标记将会消除,绿色箭头变为白色。为了去除白色箭头,需单 击“continue”按钮使程序继续运行。 然后调试子函数。打开 strd_fun.m 文件,在第 5 行设置一个断点,采用前面的向量 v 再 一次调用函数 strd_v,这时单击函数文件 strd_v.m 的标签或者在 stack 下拉列表中选择 strd_v, 则在文件的第 5 行前增加了一个白色的箭头,如图 4-12 所示,表示在主程序中的断点位置。 查看变量值为: K>> i i= 1 K>> t t= 6.2500 2.2500 0.2500 0.2500 2.2500 6.2500 图 4-12 主程序的断点设置情况 可见,错误出在 t 的计算式上,观察发现 t=t+((x-y).^2)中的 x 应改为 x(i)。 –114– 第 4 章 MATLAB 7.0 基本编程 退出调试后修改程序,清除断点,重新运行后得到正确的结果,代码设置如下: >> strd_v(v) ans = 1.8708 本例只是为了说明一些命令的用法,实际中遇到的问题可能会更复杂,要求调试者必须 要有耐心和细心。 4.4 函数的设计和实现 前面介绍了 MATLAB 的语法规则和使用方法,本节将通过一个示例具体来讲述如何用 MATLAB 来解决问题。 【例 4.2】用 MATLAB 对基带数字通信系统进行仿真。通信系统结构如图 4-13 所示,整 个系统模拟了发送和接收的全过程。其中调制部分采用 16QAM,并且采用基带传输,不必 调制到载频上(在实际操作中通信系统应该对基带信号进行载波调制,可以减少传输中的 损耗,并且提高信道频带的利用率)。该系统采用了高斯信道,在接收端进行解调并与发送 信号比较得到误码率。图 4-13 中还要求得到各个模块的输出结果图示。此外,在发送端的 低通滤波后,有一个调制到载波的步骤,主要是为了观察实际系统中的波形,这是相对独 立的一部分。 图 4-13 通信系统仿真框图 4.4.1 建立数学模型 建立模型前先对要解决的问题作细致的分析,弄清楚系统的输入/输出和工作过程,然后 –115– MATLAB 7.0 从入门到精通 逐步建立数学模型。在本例中需要对通信系统中的各个部分进行分析和建模。首先要产生随 机数,可以用 MATLAB 自带的函数实现,然后根据格雷码的编码原则实现基带的 QAM 调制。 插值(过采样)是为了后面经过低通时更好的成形滤波的需要,相当于滤波后采样点数 增加,从而使波形更平滑。实际的通信系统中有两倍的、四倍的和八倍的过采样方式,本系 统中采样八倍过采样,虽然倍数越高得到的波形越平滑,系统的性能越好,但是这样会增加 运算量。在设备条件一定的情况下,可能会影响到通信的实时性,尤其是载波调制时,这个 问题更为明显,本系统采用 8 倍过采样是一种折中的做法。 实际信道中传输的都是模拟信号,所以插值之后需要进行滤波,实现 D/A 转换,在 MATLAB 中所有的计算都是离散的,这样经过数字滤波器后仍是一个离散序列。程序中采用 平方根升余弦滚降低通滤波器来进行发送端的低通滤波和接收端的匹配滤波,滚降系数取 0.5,由于滤波器的响应出现了一个群延迟,所以在作图的时候需要通过将输入信号延迟一下 做补偿,补偿值为 delay*fd。 信号在信道中传输时会受到噪声的影响,本系统中用叠加高斯白噪声的方法来模拟这个 现象,程序中用函数(awgn)来实现。 经过信道的传输信号到达接收端,首先要经过匹配滤波器,去掉噪声的影响。对于平方 根升余弦滚降低通滤波器来讲,匹配滤波器仍是它本身。 由于过采样的过程中加入了多余的点,而实际发送的信号并不包括这些信号,所以接收 的时候应该通过采样的方式将实际发送的信号点提取出来。采样时应该注意与发送信号的同 步,这里在滤波的过程中前后分别加了 24 个冗余的点,采样时应该把这些点排除在外。解调 部分根据欧氏距离最小的原则对采样后的信号进行判决,然后按照调制时的规则进行逆变换, 每一个符号分成两个比特,然后可以计算实际的误比特率 ber 等于接收的错误比特数除以总 比特数。 对于加载波部分,程序中采用 10 个点表示一个正弦波形。因为要求载波频率是信号频 率(指的是 I 路和 Q 路分离后的频率)的 10 倍,随机序列经过 I 路和 Q 路分离后,频率为 1Hz,映射后变为 0.5Hz,插点后频率扩大八倍变为 4Hz,而载波频率为 16Hz,所以两个点 之间应该有 4 个周期的三角函数波形,每两个点之间对应有载波的 40 个点。为了做到矩阵运 算的匹配需要将信号数组作 40 倍扩展,反映到实际电路中应该加保持电路,而这样会增加很 多高频成分,往往加低通滤波对波形进行平滑,本系统中不做考虑。 4.4.2 编写代码 数学模型已经基本讨论完毕,下面介绍实现的代码。整个系统是通过一个主函数和若干 个子函数实现的,这样便于调试,也增强了可读性。这些子函数包括 QAM 调制函数 (Qam_modulation.m)、绘制星座图(plot_astrology.m)、插值(insert_value.m)、绘制正交信 号图(plot_2way.m)、绘制滤波后的信号图(stem_2way.m)、升余弦滤波(rise_cos.m)、调 制到载波(modulate_to_high.m)、叠加高斯噪声(generate_noise.m)、采样(pick_sig.m)、解 调(demodulate_sig.m)和误码率曲线作图(plot_snr.m)。下面将具体介绍这些函数。 1.主函数:project.m function project(N,p) –116– %====N 为待仿真序列的维数 %====p 为产生’1’的概率 %============================== %首先产生随机二进制序列 source=randsrc(1,N,[1,0;p,1-p]); %============================== %对产生的二进制序列进行 QAM 调制 [source1,source2]=Qam_modulation(source); %=============================== %画出星座图 figure(1); plot_astrology(source1,source2); %=============================== %两路信号进行插值 sig_insert1=insert_value(source1,8); sig_insert2=insert_value(source2,8); %=============================== %画出两路信号的波形图 figure(2); plot_2way(sig_insert1,sig_insert2,length(sig_insert1),0.5); title(’两路信号的波形图’); %=============================== %通过低通滤波器 [sig_rcos1,sig_rcos2]=rise_cos(sig_insert1,sig_insert2,0.25,2); %=============================== %画出两路信号的波形图 figure(3); plot_2way(sig_rcos1,sig_rcos2,length(sig_rcos1)/4,0.5); hold on stem_2way(sig_insert1,sig_insert2,3,0.25,2,length(sig_rcos1)/4); title(’通过低通滤波器后两路信号波形图’); %stem_2way(sig_insert1,sig_insert2,length(sig_insert1)/4,0.5); %=============================== %====将基带信号调制到高频上 [t,sig_modulate]=modulate_to_high(sig_rcos1,sig_rcos2,0.25,2.5); figure(4); plot(t(1:500),sig_modulate(1:500)); %=============================== %====将滤波后的信号加入高斯白噪声 snr=10; 第 4 章 MATLAB 7.0 基本编程 –117– MATLAB 7.0 从入门到精通 [x1,x2]=generate_noise(sig_rcos1,sig_rcos2,snr); sig_noise1=x1’; sig_noise2=x2’; %end; figure(5) plot_2way(sig_noise1,sig_noise2,length(sig_noise1)/4,0.5); %=============================== %====经过匹配滤波器 [sig_match1,sig_match2]=rise_cos(sig_noise1,sig_noise2,0.25,2); figure(6); plot_2way(sig_match1,sig_match2,length(sig_match1)/4,0.5); %=============================== %采样 [x1,x2]=pick_sig(sig_match1,sig_match2,8); sig_pick1=x1; sig_pick2=x2; %画出星座图 figure(7) plot_astrology(sig_pick1,sig_pick2); %解调 signal=demodulate_sig(sig_pick1,sig_pick2); %画出 figure(8) plot_snr; 2.QAM 调制函数 Qam_modulation.m function [y1,y2]=Qam_modulation(x) %QAM_modulation %对产生的二进制序列进行 QAM 调制 %=====首先进行串并转换,将原二进制序列转换成两路信号 N=length(x); a=1:2:N; y1=x(a); y2=x(a+1); %=====分别对两路信号进行 QPSK 调制 %======对两路信号分别进行 2~4 电平变换 a=1:2:N/2; temp1=y1(a); temp2=y1(a+1); y11=temp1*2+temp2; –118– temp1=y2(a); temp2=y2(a+1); y22=temp1*2+temp2; %=======对两路信号分别进行相位调制 a=1:N/4; y1=(y11*2-1-4)*1.*cos(2*pi*a); y2=(y22*2-1-4)*1.*cos(2*pi*a); y1(find(y11==0))=-3; y1(find(y11==1))=-1; y1(find(y11==3))=1; y1(find(y11==2))=3; y2(find(y22==0))=-3; y2(find(y22==1))=-1; y2(find(y22==3))=1; y2(find(y22==2))=3; 3.绘制星座图 plot_astrology.m function plot_astrology(a,b) %画出星座图 figure(1) subplot(1,1,1) plot(a,b,’*’); axis([-5 5 -5 5]); line([-5,5],[0,0],’LineWidth’,3,’Color’,’red’); line([0,0],[-5,5],’LineWidth’,3,’Color’,’red’); title(’QAM 星座图’); 4.插值 insert_value.m function y=insert_value(x,ratio) %两路信号进行插值 y=zeros(1,ratio*length(x)); a=1:ratio:length(y); y(a)=x; 5.绘制正交信号图 plot_2way.m function y=plot_2way(x1,x2,len,t) mm=150; subplot(2,1,2); plot((1:len)*t,x2(1:len)); axis([0 len*t -4 4]); 第 4 章 MATLAB 7.0 基本编程 –119– MATLAB 7.0 从入门到精通 hold on plot((1:len)*t,x2(1:len),’.’,’color’,’red’); hold off xlabel(’虚部信号’); subplot(2,1,1); plot((1:len)*t,x1(1:len)); axis([0 len*t -4 4]); hold on plot((1:len)*t,x1(1:len),’.’,’color’,’red’); xlabel(’实部信号’); hold off 6.绘制滤波后的信号图 stem_2way.m function stem_2way(x1,x2,delay,fd,fs,len) subplot(2,1,1); hold on stem(((1:len)+fs/fd*3)/fs,x1(1:len)); subplot(2,1,2); hold on stem(((1:len)+fs/fd*3)/fs,x2(1:len)); 7.升余弦滤波 rise_cos.m function [y1,y2]=rise_cos(x1,x2,fd,fs) [yf, tf]=rcosine(fd,fs, ’fir/sqrt’); [yo1, to1]=rcosflt(x1, fd,fs,’filter/Fs’,yf); [yo2, to2]=rcosflt(x2, fd,fs,’filter/Fs’,yf); y1=yo1; y2=yo2; 8.调制到载波 modulate_to_high.m function [t,y]=modulate_to_high(x1,x2,f,hf) yo1=zeros(1,length(x1)*hf/f*10); yo2=zeros(1,length(x2)*hf/f*10); n=1:length(yo1); yo1(n)=x1(floor((n-1)/(hf/f*10))+1); yo2(n)=x1(floor((n-1)/(hf/f*10))+1); t=(1:length(yo1))/hf*f/10; y=yo1.*cos(2*pi*hf*t)-yo2.*sin(2*pi*hf*t); 9.叠加高斯噪声 generate_noise.m function [y1,y2]=generate_noise(x1,x2,snr) –120– snr1=snr+10*log10(4);%符号信噪比 ss=var(x1+i*x2,1); y=awgn([x1+j*x2],snr1+10*log10(ss/10),’measured’); y1=real(y); y2=imag(y); 第 4 章 MATLAB 7.0 基本编程 10.采样 pick_sig.m function [y1,y2]=pick_sig(x1,x2,ratio) y1=x1(ratio*3*2+1:ratio:(length(x1)-ratio*3*2)); y2=x2(ratio*3*2+1:ratio:(length(x1)-ratio*3*2)); 11.解调 demodulate_sig.m function y=demodulate_sig(x1,x2) xx1(find(x1>=2))=3; xx1(find((x1<2)&(x1>=0)))=1; xx1(find((x1>=-2)&(x1<0)))=-1; xx1(find(x1<-2))=-3; xx2(find(x2>=2))=3; xx2(find((x2<2)&(x2>=0)))=1; xx2(find((x2>=-2)&(x2<0)))=-1; xx2(find(x2<-2))=-3; temp1=zeros(1,length(xx1)*2); temp1(find(xx1==-1)*2)=1; temp1(find(xx1==1)*2-1)=1; temp1(find(xx1==1)*2)=1; temp1(find(xx1==3)*2-1)=1; 12.误码率曲线作图 plot_snr.m clear; %用来仿真 QAM 的误 bit 率 snr=1:1:11; %先来计算理论误 bit 率 error_theory=(1-(1-(2*(1-1/sqrt(16))*1/2*erfc(1/sqrt(2)*sqrt(3*4*10.^(snr/10)/(16-1))))).^2)/4; %用理论的误 bit 率来决定需要仿真的点数 N=floor(1./error_theory)*100+100; N(find(N<5000))=5000; %开始仿真 global p; for i=1:length(N); %首先产生随机二进制序列 –121– MATLAB 7.0 从入门到精通 source=randsrc(1,N(i),[1,0;p,1-p]); %对产生的二进制序列进行 QAM 调制 [source1,source2]=Qam_modulation(source); %插值 sig_insert1=insert_value(source1,8); sig_insert2=insert_value(source2,8); [source1,source2]=rise_cos(sig_insert1,sig_insert2,0.25,2); %====将滤波后的信号加入高斯白噪声 [x1,x2]=generate_noise(source1’,source2’,snr(i)); sig_noise1=x1’; sig_noise2=x2’; [sig_noise1,sig_noise2]=rise_cos(sig_noise1,sig_noise2,0.25,2); [x1,x2]=pick_sig(sig_noise1,sig_noise2,8); sig_noise1=x1; sig_noise2=x2; %解调 signal=demodulate_sig(sig_noise1,sig_noise2); %计算误 bit 率 error_bit(i)=length(find(signal-source)~=0)/N(i); end; %画出图形 semilogy(snr,error_bit,’-b’); hold on semilogy(snr,error_theory,’-r’) 4.4.3 运行程序 运行程序,即在命令行中输入 project(40000,0.5),此数值可以根据需要进行选择。如果 有错误则进行调试。正确的运行结果如图 4-14~图 4-21 所示。 –122– 第 4 章 MATLAB 7.0 基本编程 QAM星座图 5 4 3 2 1 0 -1 -2 -3 -4 -5 -5 -4 -3 -2 -1 0 1 2 3 4 5 图 4-14 QAM 星座图 两路信号的波形图 4 2 0 -2 -4 0 50 100 150 实部信号 4 2 0 -2 -4 0 50 100 150 虚部信号 图 4-15 经过插值后的两路信号波形图 –123– MATLAB 7.0 从入门到精通 4 2 0 -2 -4 0 50 100 150 实部信号 通过低通滤波器后两路信号波形图 4 2 0 -2 -4 0 50 100 150 虚部信号 图 4-16 通过低通滤波后的两路信号波形图 0.03 0.02 0.01 0 -0.01 -0.02 -0.03 0 1 2 3 4 5 图 4-17 载波调制信号图 –124– 第 4 章 MATLAB 7.0 基本编程 4 2 0 -2 -4 0 50 100 150 实部信号 4 2 0 -2 -4 0 50 100 150 虚部信号 图 4-18 加入高斯白噪声的两路信号波形 4 2 0 -2 -4 0 50 100 150 实部信号 4 2 0 -2 -4 0 50 100 150 虚部信号 图 4-19 经过匹配滤波器后的波形 –125– MATLAB 7.0 从入门到精通 QAM星座图 5 4 3 2 1 0 -1 -2 -3 -4 -5 -5 0 5 图 4-20 信噪比为 10dB 时的星座图 由图 4-20 可见,当信噪比为 10dB 时,解调出来的信号大部分是正确的,基本上集中在 发送信号的周围。 图 4-21 中的横坐标是信噪比,信噪比越大,误码率越低,这一点符合通信系统的规律。 (其中上面一条是实际的误码率曲线,下面一条是理论曲线,二者基本吻合,说明程序是正确 的。) 0 10 -1 10 -2 10 -3 10 –126– -4 10 0 2 4 6 8 10 12 图 4-21 误码率曲线 第 4 章 MATLAB 7.0 基本编程 从本例中可以看出,MATLAB 系统仿真的功能是十分强大的,重要的是建立正确的数学 模型。在编写程序时,应尽量将重复用到的部分编写成独立的函数。同时,为了程序的可扩 展性,应该尽量避免使用全局变量。各个函数可单独调试,调试正确后再统一运行,如果仍 不正确,则需要认真考虑算法是否合理。 –127– 第 5 章 数据可视化 不管根据计算得到的数据堆或是符号堆是多么的准确,人们还是很难从这一大堆原始的 数据和符号中发现它们的具体物理含义或是内在规律,而数据图形恰能使视觉感官直接感受 到数据的许多内在本质,发现数据的内在联系。因此数据可视化是一项非常重要的技术。 MATLAB 可以表达出数据的二维、三维和四维的图形。通过对图形的线型、立面、色彩、 光线、视角等属性的控制,可把数据的内在特征表现得更加细腻完善。下面将向读者介绍这 些图形绘制和图形处理的命令。 5.1 二维绘图 二维图形的绘制是 MATLAB 语言图形处理的基础,也是在绝大多数数值计算中广泛应 用的图形方式之一。本小节主要向读者介绍 plot、fplot、ezplot 三个基本的二维绘图命令。 5.1.1 plot 命令 绘制二维图形最常用的函数就是 plot 函数,通过不同形式的输入,该函数可以实现不同 的功能。其调用格式有如下 3 种: 1.plot(y) 此命令中参数 y 可以是向量、实数矩阵或复数向量。若 y 为向量,则绘制的图形以向量 索引为横坐标值、以向量元素的值为纵坐标值;若 y 为实数矩阵,则绘制 y 的列向量对其坐 标索引的图形;若 y 为复向量,则 plot(y)相当于 plot(real(y),imag(y)。在后面介绍的两种调 用格式中,元素的虚部将被忽略。 例如用 plot(y)命令绘制向量,如图 5-1 所示,具体代码设置如下: >>t=1:0.1:10; >>y=sin(t); >>plot(y) 1 0.8 0.6 0.4 0.2 0 -0.2 -0.4 -0.6 -0.8 -1 0 10 20 30 40 50 60 70 80 90 100 图 5-1 plot(y)绘制向量 第 5 章 数据可视化 注意:图 5-1 中横坐标的范围不是[1,10],而是[1,91],因为 plot(y)以向量索引为横坐标值。 例如用 plot(y)命令绘制矩阵,如图 5-2 所示,具体代码设置如下: >>y= [0 1 2;2 3 4;5 6 7] >>plot(y) 7 6 5 4 3 2 1 0 1 1.2 1.4 1.6 1.8 2 2.2 2.4 2.6 2.8 3 图 5-2 plot(y)绘制矩阵 例如用 plot(y)命令绘制复向量,如图 5-3 所示,具体代码设置如下: >>x=[1:1:100]; >>y=[2:2:200]; >>z=x+y.*i; >>plot(z) 200 180 160 140 120 100 80 60 40 20 0 0 10 20 30 40 50 60 70 80 90 100 2.plot(x, y) 图 5-3 plot(y)绘制复向量 x,y 均可为向量和矩阵,其中有 3 种组合用于绘制连线图。 x,y 均为 n 维向量时,绘制向量 y 对向量 x 的图形,即以 x 为横坐标,y 为纵坐标。 x 为 n 维向量,y 为 m×n 或 n×m 的矩阵时,该命令将在同一图内绘得 m 条不同颜色的 连线。图中以向量 x 为 m 条连线的公共横坐标,纵坐标为 y 矩阵的 m 个 n 维分量。 x,y 均为 m×n 矩阵时,将绘得 n 条不同颜色的连线。绘制规则为:以 x 矩阵的第 i 列 分量作为横坐标,矩阵 y 的第 i 列分量作为纵坐标,绘得第 i 条连线。 例如用 plot(x, y)绘制双向量,如图 5-4 所示,代码设置如下: >>x=0:0.1:10; >>y=sin(x)+2; –129– MATLAB 7.0 从入门到精通 >>plot(x,y) 3 2.8 2.6 2.4 2.2 2 1.8 1.6 1.4 1.2 1 0 1 2 3 4 5 6 7 8 9 10 图 5-4 plot(x ,y)绘制双向量 例如用 plot(x,y)绘制向量和矩阵,如图 5-5 所示,代码设置如下: >>x=0:0.1:10; >>y=[sin(x)+2;cos(x)+1]; >>plot(x,y) 3 2.5 2 1.5 1 0.5 0 0 1 2 3 4 5 6 7 8 9 10 图 5-5 plot(x ,y)绘制向量和矩阵 例如用 plot(x, y)绘制双矩阵,如图 5-6 所示,代码设置如下: >>x=[1 2 3;4 5 6;7 8 9;2 3 4;5 6 7]; >>y=[2 4 5;3 6 7;4 6 8;1 3 5;2 6 3]; >>plot(x,y) 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 图 5-6 plot(x, y)绘制双矩阵 3.plot(x, y, s) 此格式用于绘制不同的线型、点标和颜色的图形,其中 s 为字符,可以代表不同的线型、 点标和颜色。常见的可用字符及其意义如表 5-1 所示。 –130– 表 5-1 二维绘图的图形常用设置选项 选项 说明 选项 - 实线 . : 点线 o -. 点划线 + -- 虚线 * y 黄色 x m 紫红色 s c 蓝绿色 d r 红色 v g 绿色 ^ b 蓝色 < w 白色 > k 黑色 p 例如用 plot(x, y, s)绘图,如图 5-4 所示,代码设置如下: >>x=0:0.5:20; >>y=sin(x); >>plot(x,y,’-.rd’) 1 0.8 0.6 0.4 0.2 0 -0.2 -0.4 -0.6 -0.8 -1 0 2 4 6 8 10 12 14 16 18 20 图 5-7 plot(x, y, s)绘图 第 5 章 数据可视化 说明 点 圆 加号 星号 x 符号 方形 菱形 下三角 上三角 左三角 右三角 正五边形 5.1.2 fplot 命令 前面介绍的 plot 命令是将从外部输入或通过函数数值计算得到的数据矩阵转化为连线 图。而在实际的应用中,用户可能并不知道某一个函数随自变量变化的趋势,此时若采用 plot 命令来绘图,则有可能会因为自变量的取值间隔不合理而使曲线图形不能反应出自变量在某 些区域内函数值的变化情况。当然用户可以将自变量间隔取得足够小以体现函数值随自变量 变化的精确曲线,但是这样会使数据量变大。 而 fplot 命令就可以很好地解决这个问题。fplot 命令用于指导如何通过函数来取得绘图 的数值点矩阵。该命令通过内部的自适应算法来动态决定自变量的取值间隔,当函数值变化 缓慢时,间隔取大一点;变化剧烈时(即函数的二阶导数很大),间隔取小一点。 该命令的使用格式如下: –131– MATLAB 7.0 从入门到精通 fplot(function,limits) fplot(function,limits,LineSpec) fplot(function,limits,tol) fplot(function,limits,tol,LineSpec) fplot(function,limits,n) fplot(axes_handle,...) [X,Y] = fplot(function,limits,...) [...] = fplot(function,limits,tol,n,LineSpec,P1,P2,...) 下面介绍其中各项参数的含义。 • function:待绘制的函数的名称; • limits:定义 x 轴(自变量)的取值范围,或 x 轴和 y 轴(应变量)的范围[xmin xmax], [xmin xmax ymin ymax]; • LineSpec:定义绘图的线型、颜色和数据点等(如表 5-1 所示); • tol:相对误差容忍度,默认值为 2e-3; • n:当 n≥1 时,至少绘制 n+1 个点,默认值为 1。最大的步长限制为(1/n)*(xmax-xmin); • axes_handle:坐标轴句柄,函数的图形将绘制在这个坐标系中; • P1,P2…:向函数传递参数值。 【例如】 • 创建函数,并保存为 myfun.m,具体代码设置如下: function Y= myfun(x) Y=200*sin(1./tan(pi.*x)); • 用 fplot 命令求得坐标点,并按同样大小创建一个等间隔坐标点,代码设置如下: >>[X,Y]=fplot(‘myfun’,[-0.1 0.1],2e-4) >>L=size(X); >>m=-0.1:0.2/(L(1)+1):0.1; >>n=myfun(m); • 作图比较 fplot 命令与 plot 命令,代码设置如下:: >>plot(X,Y) >>figure //重新打开一个绘图窗口 >>plot(m,n) 用 fplot 命令和 plot 命令绘制出的结果如图 5-8 和图 5-9 所示。 200 150 100 50 0 -50 -100 -150 -200 -0.1 -0.08 -0.06 -0.04 -0.02 0 0.02 0.04 0.06 0.08 0.1 图 5-8 fplot 命令绘图结果 –132– 200 150 100 50 0 -50 -100 -150 -200 -0.1 -0.08 -0.06 -0.04 -0.02 0 0.02 0.04 0.06 0.08 0.1 图 5-9 plot 命令绘图结果 第 5 章 数据可视化 5.1.3 ezplot 命令 ezplot 命令也是用于绘制函数在某一自变量区域内的图形。该命令的使用格式如下: ezplot(f) ezplot(f,[min,max]) ezplot(f,[xmin,xmax,ymin,ymax]) ezplot(x,y) ezplot(x,y,[tmin,tmax]) ezplot(...,figure_handle) ezplot(axes_handle,...) h = ezplot(...) 当 f = f(x)时,各参数的含义如下。 • ezplot(f):绘制表达式 f=f(x)在默认区域 2*pi>ezplot(’ x^2+y^2-4’, [-3,3,-3,3]); 用 ezplot 命令绘制出来的结果如图 5-10 所示。 x2+y2-4 = 0 3 2 1 y 0 -1 -2 -3 -3 -2 -1 0 1 2 3 x 图 5-10 ezplot 命令作图 5.2 三维绘图 在实际的工程计算中常常需要将结果表示成三维图形,MATLAB 语言为此提供了相应的 三维图形的绘制功能。这些绘制功能与二维图形的绘制有很多类似之处,其中曲线的属性设 置完全相同。最常用的三维绘图是绘制三维曲线图、三维网格图和三维曲面图 3 种基本类型, 相应的 MATLAB 命令为 plot3、mesh 和 surf,下面分别介绍它们的具体使用方法。 5.2.1 plot3 命令 与 plot 类似,plot3 是三维绘图的基本函数,其调用格式如下: plot3(X1,Y1,Z1,...) plot3(X1,Y1,Z1,LineSpec,...) plot3(...,’PropertyName’,PropertyValue,...) h=plot3(...) 其中 X1,Y1,Z1 为向量或矩阵,LineSpec 定义曲线线型、颜色和数据点等(参见表 5-1), PropertyName 为线对象的属性名,PropertyValue 为相应属性的值,h 是用于存放曲线簇中每 一个线对象的句柄的变量。 当 X1,Y1,Z1 为长度相同的向量时,plot3 命令将绘得一条分别以向量 X1,Y1,Z1 为 x,y, z 轴坐标值的空间曲线; 当 X1,Y1,Z1 均为 m×n 的矩阵时,plot3 命令将绘得 m 条曲线。其第 i 条空间曲线分别 以 X1,Y1,Z1 矩阵的第 i 列分量为 x,y,z 轴坐标值的空间曲线。 例如用 plot3 命令绘制螺旋线,如图 5-11 所示,代码设置如下: >>t=0:pi/50:10*pi; >>plot3(cos(t),sin(t),t) –134– 40 30 20 10 0 1 0.5 0 -0.5 -1 -1 0 -0.5 1 0.5 图 5-11 螺旋线 例如用 plot3 命令绘制向量,如图 5-12 所示,具体代码如下: >>t=[0:pi/100:2*pi]; >>x=[sin(t) sin(t)]; >>y=[cos(t) cos(t)]; >>z=[(sin(t)).^2+(cos(t)).^2 (sin(t)).^2+(cos(t)).^2+1]; >>plot3(x,y,z) 第 5 章 数据可视化 2 1.8 1.6 1.4 1.2 1 0.8 1 0.5 0 -0.5 -1 -1 0 -0.5 1 0.5 图 5-12 plot3 绘制向量 5.2.2 mesh 命令 该命令与 plot3 不同的是它可以绘出在某一区间内完整的曲面,而不是单根曲线。其调 用格式如下: mesh(X,Y,Z) mesh(Z) mesh(...,C) mesh(...,’PropertyName’,PropertyValue,...) mesh(axes_handles,...) h = mesh(...) hsurface = mesh(’v6’...) 其中 C 用于定义颜色,如果没有定义 C,则 mesh(X,Y,Z)绘制的颜色随 Z 值(即曲面高 度)成比例变化。X 和 Y 必须均为向量,若 X 和 Y 的长度分别为 m 和 n,则 Z 必须为 m×n 的矩阵,也即[m,n]=size(Z),在这种情况下,网格线的顶点为(X(j),Y(i),Z(i,j));若参数中 –135– MATLAB 7.0 从入门到精通 没有提供 X,Y,则将(i,j)作为 Z(i,j)的 X,Y 轴坐标值。 例如用 mesh 函数绘制三维曲线,如图 5-13 所示,代码设置如下: >>x=-4:0.1:4;y=x’; >>m=ones(size(y))*x; >>n=y* ones(size(x)) >>p=sqrt(m.^2+n.^2)+eps; >>z=sin(p)./p >>mesh(z) 图 5-13 mesh 函数绘制三维曲面 例如用 mesh(z)函数绘制三维曲面,如图 5-14 所示,代码设置如下: >>x=[0:0.1:5;2:0.1:7]; >>mesh(x) 图 5-14 mesh(z)函数绘制三维曲面 5.2.3 surf 命令 该命令的调用方法与 mesh 命令类似,不同的是 mesh 函数绘制的图形是一个网格图,而 surf 命令绘制得到的是着色的三维曲面。着色的方法是在得到相应的网格后,对每一个网格 依据该网格所代表的节点的色值(由变量 C 控制)来定义这一网格的颜色。调用方式如下: surf(Z) surf(X,Y,Z) surf(X,Y,Z,C) surf(...,’PropertyName’,PropertyValue) –136– 第 5 章 数据可视化 surf(axes_handle,...) h = surf(...) hsurface = surf(’v6’,...) 其中各个参数意义与 mesh 命令中的相同。 例如分别用 surf 命令和 mesh 命令绘制着色曲面图进行比较,如图 5-15 和图 5-16 所示。 代码设置如下: >>[X,Y,Z] = peaks(30); >>surf(X,Y,Z) >> figure >> mesh(Z) 图 5-15 用 surf 命令绘制着色曲面图 图 5-16 mesh 命令绘制相同的数据 5.2.4 基本三维绘图命令的改进命令 前面介绍了 3 个基本的三维绘图命令,下面向读者介绍另外几个常用命令,它们对基本 的三维绘图命令增加了一些特别的处理图形的功能。 1.meshgrid、meshc 和 meshz meshgrid 命令的作用是将给定的区域按一定的方式划分成平面网格,该平面网格可以用 来绘制三维曲面,调用方法如下: [X,Y]=meshgrid(x,y) 其中 x 和 y 为给定的向量,是用来定义网格划分区域的,也可定义网格划分方法;X,Y 用来存储网格划分后的数据矩阵。 meshc 是在用 mesh 命令绘制的三维曲面图下绘出等高线,调用方式与 mesh 相同。 例如用 meshc 命令绘制三维曲面,如图 5-17 所示。代码设置如下: >>[X,Y,Z] = peaks(30); >> meshc(Z) –137– MATLAB 7.0 从入门到精通 图 5-17 meshc 命令绘制三维曲面 meshz 是在 mesh 函数的作用之上增加绘制边界面的功能,调用方式与 mesh 一样。 例如用 meshz 命令绘制三维曲面,如图 5-18 所示。代码设置如下: >>[X,Y,Z] = peaks(30); >> meshz(Z) 图 5-18 meshz 命令绘制三维曲面 2.surfc surfc 与 meshc 类似,在 surf 命令绘制的图形上加绘等高线,调用方式与 surf 命令一样。 例如用 surfc 命令绘制三维曲面,如图 5-19 所示。代码设置如下: >>[X,Y,Z] = peaks(30); >> surfc(Z) 图 5-19 surfc 命令绘制三维曲面 3.waterfall 该命令用于绘制形似瀑布流水形状的网线图,调用格式与 mesh 相同。 –138– 第 5 章 数据可视化 例如用 waterfall 命令绘制三维曲面,如图 5-20 所示。代码设置如下: >>[X,Y,Z] = peaks(30); >> waterfall(Z) 图 5-20 waterfall 命令绘制三维曲面 5.3 特殊图形 5.3.1 二维特殊图形函数 常见的特殊二维图形函数如表 5-2 所示。 表 5-2 二维特殊图形函数 函数名 说明 函数名 说明 area 填充绘图 fplot 函数绘制 bar 条形图 hist 柱状图 barh 水平条形图 pareto Pareto 图 comet 彗星图 pie 饼状图 errorbar 误差带图 plotmatrix 分散矩阵绘制 ezplot 简单绘制函数图 ribbon 三维图的二维条状显示 ezpolar 简单绘制极坐标图 scatter 散射图 feather 矢量图 stem 离散序列火柴杆状图 fill 多边形填充 stairs 阶梯图 gplot 拓扑图 rose 极坐标系下的柱状图 compass 与 feather 功能类似的矢量图 quiver 向量场 表中的函数均有不同的调用方法,下面通过几个示例向读者介绍其中几个常用的命令, 其他函数的使用方法读者可以查阅 MATLAB 7.0 的在线帮助。 【bar 命令】 该命令用于绘制二维垂直条形图,用垂直条形显示向量或矩阵中的值。调用格式如下: bar(y) //为每一个 y 中的元素画一个条状 bar(x,y) //在指定的横坐标 x 上画出 y,其中 x 为严格单增的向量。若 y 为矩阵,则 bar 把矩阵分解成 几个行向量,在指定的横坐标处分别画出。 –139– MATLAB 7.0 从入门到精通 bar(...,width) //设置条形的相对宽度和控制在一组内条形的间距。默认值为 0.8,所以如果用户没有指 定 x,则同一组内的条形有很小的间距,若设置 width 为 1,则同一组内的条形相互接触 bar(...,’style’) //style 定义条的形状类型,可以取值’group’或’stack’。其中“group”为默认的显示模式。 “group”表示若 y 为 n*m 阶的矩阵,则 bar 显示 n 组,每组有 m 个垂直条形的条形图。“stack”表示对矩阵 y 的每一个行向量显示在一个条形中,条形的高度为该行向量中的分量和。其中同一条形中的每个分量用不 同的颜色显示出来,从而可以显示每个分量在向量中的分布 bar(...,’bar_color’) //’bar_color’定义条的颜色 bar(axes_handle,...) h = bar(…) //返回一个 patch 图形对象句柄的向量。每一条形对应一个句柄 【示例 1】 >>x=-2.9:0.2:2.9; >>bar(x,exp(-x.*x),’r’) 用函数 bar 绘制的条形图如图 5-21 所示。 图 5-21 条形图 【示例 2】 >>Y = round(rand(5,3)*10); >>subplot(2,2,1) >>bar(Y,’group’) >>title ’Group’ >>subplot(2,2,2) >>bar(Y,’stack’) >>title ’Stack’ >>subplot(2,2,3) >>barh(Y,’stack’) >>title ’Stack’ >>subplot(2,2,4) >>bar(Y,1.5) >>title ’Width = 1.5’ 函数 bar 绘图命令如图 5-22 所示。 –140– 第 5 章 数据可视化 图 5-22 bar 绘图命令 【pie 命令】 该命令用于绘制饼形图。调用格式如下: pie(x) pie(x, explode) // explode 与 x 同维的矩阵,若其中有非零元素,x 矩阵中的相应位置的元素在饼图中 对应的扇形将向外移出一些,加以突出 pie(...,labels) // labels 用于定义相应块的标签 pie(axes_handle,...) h = pie(...) 【示例 1】 >>x = [1 3 0.5 2.5 2]; >>explode = [0 1 0 0 0]; >>pie(x,explode) 用 pie 命令绘制饼状图如图 5-23 所示。 图 5-23 pie 绘制饼状图 【示例 2】 >>pie(1:3,{’Taxes’,’Expenses’,’Profit’}) 函数 pie 绘制命令如图 5-24 所示。 图 5-24 pie 绘图命令 –141– MATLAB 7.0 从入门到精通 【hist 命令】 该命令用于绘制二维条形直方图,可以显示出数据的分布情况。所有向量 y 中的元素或 者是矩阵 y 的列向量中的元素是根据它们的数值范围来分组的,每一组作为一个条形进行显 示。条形直方图中的 x 轴反映了数据 y 中元素数值的范围,直方图的 y 轴显示出参量 y 中的 元素落入该组的数目。调用格式如下: n = hist(y) //把向量 y 中的元素放入等距的 10 个条形中,且返回每一个条形中的元素个数。若 y 为矩 阵,则该命令按列对 y 进行处理 n = hist(y,x) //参量 x 为向量,把 y 中元素放到 m(m=length(x))个由 x 中元素指定的位置为中心的条 形中 n = hist(y,nbins) //参量 nbins 为标量,用于指定条形的数目 [n,xout] = hist(...) //返回向量 n 与包含频率计数与条形的位置向量 xout,用户可以用命令 bar(xout,n)画 出条形直方图 hist(...) hist(axes_handle,...) 【示例】 >>x = -2.9:0.1:2.9; >>y = randn(10000,1); >>hist(y,x) 用 hist 命令绘制图形如图 5-25 所示。 图 5-25 hist 命令绘图 【contour 命令】 该命令用于绘制等高线图。调用格式如下: contour(Z) contour(Z,n) contour(Z,v) contour(X,Y,Z) contour(X,Y,Z,n) contour(X,Y,Z,v) contour(...,LineSpec) [C,h] = contour(...) [C,h] = contour(’v6’,...) –142– 第 5 章 数据可视化 其中,输入变量 Z 必须为一数值矩阵,是必须输入的变量;n 为所绘图形等高线的条数; v 为向量,等高线条数等于该向量的长度,并且等高线的值为对应向量的元素值;c 为等高线 矩阵;h 为等高线的句柄。若没有选择,系统将自动为矩阵 z 绘制等高线图。类似的函数有 contourf,用以绘制填充的等高线图,调用格式与 contour 相同。 【示例】 >>[X,Y] = meshgrid(-2:.2:2,-2:.2:3); >>Z = X.*exp(-X.^2-Y.^2); >>[C,h] = contour(X,Y,Z); >>set(h,’ShowText’,’on’,’TextStep’,get(h,’LevelStep’)*2) 用 contour 命令绘制等高线如图 5-26 所示。 图 5-26 contour 命令绘制等高线 【quiver 命令】 该命令用于绘制矢量图或速度图,绘制向量场的形状。调用格式如下: quiver(x,y,u,v) //在坐标(x,y)点处用箭头图形绘制向量,(u,v)为相应点的速度分量。X,y,u,v 必须具有相同的大小 quiver(u,v) quiver(...,scale) // scale 是用来控制看到的图中向量“长度”的实数,默认值为 1。有时需要设置小的 值,以免绘制的向量彼此重叠掩盖 quiver(...,LineSpec) quiver(...,LineSpec,’filled’) quiver(axes_handle,...) h = quiver(...) hlines = quiver(’v6’,...) 【示例】 >>[X,Y] = meshgrid(-2:.2:2); >>Z = X.*exp(-X.^2 - Y.^2); >>[DX,DY] = gradient(Z,.2,.2); >>contour(X,Y,Z) >>hold on >>quiver(X,Y,DX,DY) –143– MATLAB 7.0 从入门到精通 用 quiver 绘制矢量图如图 5-27 所示。 图 5-27 quiver 命令绘制矢量图 【comet 命令】 该命令用于绘制二维彗星图。彗星图为彗星头(一个小圆圈)沿着数据点前进的动画, 彗星体为跟在彗星头后面的痕迹,轨道为整个函数的实线。要注意的是,由命令 comet 生成 的轨迹图的擦除模式(EraseMode)属性值为 none,该属性使得用户不能打印该图形(只能 得到彗星头),且当用户改变窗口的大小时,动画将消失。调用格式如下: comet(y) //彗星图动画显示向量 y 确定的路线 comet(x,y) //彗星图动画显示向量 x 与 y 确定的路线 comet(x,y,p) //指定彗星体的长度 p*length(y),默认的 p 值为 0.1 comet(axes_handle,...) 【示例】 >>t = 0:.01:2*pi; >>x = cos(2*t).*(cos(t).^2); >>y = sin(2*t).*(sin(t).^2); >>comet(x,y); 用 comet 命令绘制彗星图如图 5-28 所示。 图 5-28 comet 命令绘制彗星图 【errorbar 命令】 该命令用于绘制沿着一曲线画误差棒形图。误差棒为数据的置信水平或者为沿着曲线的 偏差。命令输入参数为矩阵时,则按列画出误差棒。调用格式如下: errorbar(Y,E) //画出向量 y,同时显示在向量 y 的每一元素之上的误差棒。误差棒为 E(i)在曲线 y 上面 与下面的距离,所以误差棒的长度为 2*E(i) –144– 第 5 章 数据可视化 errorbar(X,Y,E) // X,Y,E 必须为同型参量。若同为向量,则画出带长度为 2*E(i)、对称误差棒于曲线点 (X(i),Y(i))之处;若同为矩阵,则画出带长度为 E(i,j)、对称误差棒于曲面点(X(i,j),Y(i,j))之处 errorbar(X,Y,L,U) // X,Y,L,U 必须为同型参量。若同为向量,则在点(X(i),Y(i))处画出向下长为 L(i),向 上长为 U(i)的误差棒;若同为矩阵,则在点(X(i,j),Y(i.j))处画出向下长为 L(i,j),向上长为 U(i,j)的误差棒 errorbar(...,LineSpec) //用 LineSpec 指定的线型、标记符和颜色等画出误差棒 h = errorbar(...) //返回线图形对象的句柄向量给 h 【示例】 >>x=[0:0.2:4*pi]; >>y=sin(x); >>e=[0:1/(length(x)-1):1]; >>errorbar(x,y,e) 用 errorbar 命令绘制误差棒形图如图 5-29 所示。 图 5-29 errorbar 命令绘制误差棒形图 5.3.2 特殊的三维图形函数 特殊的三维图形函数如表 5-3 所示。 表 5-3 三维特殊图形函数 函数名 说明 函数名 说明 bar3 三维条形图 surfc 着色图与等高线图结合 comet3 三维彗星轨迹图 trisurf 三角形表面图 ezgraph3 函数控制绘制三维图 trimesh 三角形网格图 pie3 三维饼状图 waterfall 瀑布图 scatter3 三维散射图 cylinder 柱面图 stem3 三维离散数据图 sphere 球面图 quiver3 向量场 contour3 三维等高线 对比表 5-2 和表 5-3 可以发现其中有许多的绘图命令函数名很接近,只是在后面加了一 个数字 3,它代表是绘制相应的三维图形,其实现的功能和调用方法与对应的二维绘制函数 基本相同,在这里就不再做详细介绍,请读者自己查阅 MATLAB 7.0 的在线帮助。 【cylinder 命令】 该命令用于绘制圆柱图形。调用格式如下: –145– MATLAB 7.0 从入门到精通 [X,Y,Z] = cylinder //返回一半径为 1、高度为 1 的圆柱体的 x,y,z 轴的坐标值,圆柱体的圆周有 20 个距离相同的点 [X,Y,Z] = cylinder(r) //返回一半径为 r、高度为 1 的圆柱体的 x,y,z 轴的坐标值,圆柱体的圆周有 20 个距离相同的点 [X,Y,Z] = cylinder(r,n) //返回一半径为 r、高度为 1 的圆柱体的 x,y,z 轴的坐标值,圆柱体的圆周有 指定的 n 个距离相同的点 cylinder(axes_handle,...) cylinder(...) //没有任何的输出参量,直接画出圆柱体 【示例 1】 >>cylinder 用 cylinder 命令绘制圆柱图形如图 5-30 所示。 图 5-30 cylinder 命令绘制圆柱图形 【示例 2】 >>t = 0:pi/10:2*pi; >>[X,Y,Z] = cylinder(2+cos(t)); >>surf(X,Y,Z) 用 cylinder(r)命令绘制圆柱图形如图 5-31 所示。 图 5-31 cylinder(r)命令绘制圆柱图 【sphere 命令】 该命令用于生成球体。调用格式如下: sphere //生成三维直角坐标系中的单位球体。该单位球体由 20*20 个面组成 sphere(n) //在当前坐标系中画出有 n*n 个面的球体 [X,Y,Z] = sphere(...) //返回三个阶数为(n+1)*(n+1)的,直角坐标系中的坐标矩阵。该命令没有画图,只 是返回矩阵。用户可以用命令 surf(x,y,z)或 mesh(x,y,z)画出球体 –146– 【示例】 >>[m,n,p]=sphere(50); >>t=abs(p); >>surf(m,n,p,t) 用 sphere 命令绘制球体如图 5-32 所示。 第 5 章 数据可视化 图 5-32 sphere 命令绘制球体 5.3.3 特殊坐标轴的图形函数 前面已介绍了基本的二维绘图函数的使用,但它们的坐标轴刻度均为线性刻度。当实际 的数据出现指数变化时,指数变化就不能直观的从图形上体现出来,而且在进行数值比较过 程中经常会遇到双纵坐标显示的要求。为了解决这些问题,MATLAB 提供了相应的绘图函数。 1.semilogx 用该函数绘制图形时 x 轴采用对数坐标。若没有指定使用的颜色,当所画线条较多时, semilogx 将自动使用由当前轴的 ColorOrder 和 LineStyleOrder 属性指定的颜色顺序和线型顺 序来画线。调用方法如下: semilogx(Y) //对 x 轴的刻度求常用对数(以 10 为底),而 y 轴为线性刻度。若 y 为实数向量或矩阵, 则 结 合 y 列向 量 的 下 标 与 y 的 列向 量 画 出 线 条 ;若 y 为 复 数向 量 或 矩 阵 ,则 semilogx(Y) 等 价 于 semilogx(real(Y),imag(Y))。在 semilogx 的其他使用形式中,Y 的虚数部分将被忽略 semilogx(X1,Y1,X2,Y2…) //结合 Xn 和 Yn 画出线条,若其中只有 Xn 或 Yn 为矩阵,另外一个为向量, 行向量的维数等于矩阵的列数,列向量的维数等于矩阵的行数,则按向量的方向分解矩阵,再与向量结合, 分别画出线条 semilogx(X1,Y1,LineSpec1X2,Y2,LineSpec2, … ) // 按 顺 序 取 三 参 数 Xn,Yn,LineSpecn 画 线 , 参 数 LineSpecn 指 定 使 用 的 线 型 , 标 记 符 号 和 颜 色 。 用 户 可 以 混 合 使 用 二 参 数 和 三 参 数 形 式 , 如 semilogx(X1,Y1,X2,Y2,LineSpec2,X3,Y3) semilogx(…,’PropertyName’,PropertyValue,…) //对所有由 semilogx 命令生成的图形对象句柄的属性进 行设置 h = semilogx(…) //返回 line 图形句柄向量,每条线对应一个句柄 【示例】 >>x=0.001:0.01*pi:2*pi; >>y=log10(x); –147– MATLAB 7.0 从入门到精通 >> semilogx(x,y,’-*’); >>figure; >>plot(x,y) 用 semilogx 和 plot 绘制 y=log10(x)的图形如图 5-33 和图 5-34 所示。 图 5-33 semilogx 绘制 y=log10(x) 图 5-34 plot 绘制 y=log10(x) 2.semilogy 用该函数绘制图形时 y 轴采用对数坐标。调用格式与 semilogx 基本相同。 【示例】 >>x=0.001:0.01*pi:2*pi; >>y=10.^x; >> semilogy(x,y,’-*’); >>figure; >>plot(x,y) 用 semilogy 和 plot 绘制 y=10.^x 的图形如图 5-35 和图 5-36 所示。 –148– 图 5-35 semilogy 绘制 y=10.^x 第 5 章 数据可视化 图 5-36 plot 绘制 y=10.^x 3.loglog 用该函数绘制图形时 x 和 y 轴均采用对数坐标。调用格式与 semilogx 基本相同。 【示例】 >>m =0.001:0.01*pi:2*pi; >>x=10.^m; >>y=log10(m); >>loglog(x,y,’-*’); >>figure >>plot(x,y,’-.r’) 用 loglog 和 plot 绘制图形如图 5-37 和图 5-38 所示。 4.plotyy 图 5-37 loglog 绘制图形 1 0.5 0 -0.5 -1 -1.5 -2 -2.5 -3 0 2 4 6 8 10 12 14 16 18 x 105 图 5-38 plot 绘制图形 用该函数在双 y 轴坐标系中作图,调用方法如下: plotyy(X1,Y1,X2,Y2) plotyy(X1,Y1,X2,Y2,’function’) //以 function 方式绘制图形,function 可以为 plot,semilogx,semilogy 或 –149– MATLAB 7.0 从入门到精通 loglog 等 plotyy(X1,Y1,X2,Y2,’function1’,’function2’) //以 function1 方式绘制(X1,Y1), 以 function2 方式绘制 (X2,Y2) 【示例】 >>x=0:0.01*pi:2*pi; >>y=sin(x); >>z=exp(x); >>plotyy(x,y,x,z,’plot’,’semilogy’) 用 plotyy 绘制图形如图 5-39 所示。 1 103 0.8 0.6 0.4 102 0.2 0 -0.2 101 -0.4 -0.6 -0.8 -1 100 0 1 2 3 4 5 6 7 图 5-39 plotyy 绘制图形 5.极坐标、柱坐标和球坐标下绘制图形 (1)在极坐标系中绘图 【polar 命令】 该命令用于画极坐标图。它接受极坐标形式的函数 rho=f(θ),在笛卡尔坐标系平面上画 出该函数,且在平面上画出极坐标形式的格栅。调用格式如下: polar(theta,rho) //用极角 theta 和极径 rho 画出极坐标图形。极角 theta 为从 x 轴到半径的单位为弧度的 向量,极径 rho 为各数据点到极点的半径向量 polar(theta,rho,LineSpec) //参量 LineSpec 指定极坐标图中线条的线型、标记符号和颜色等 【示例】 >>rho0=1; >>theta=0:pi/20:4*pi; >>rho=rho0+theta*rho0; >>polar(theta,rho,’:’) 用 polar 命令绘制渐开线,如图 5-40 所示。 120 150 180 90 15 60 10 30 5 0 210 330 240 300 270 –150– 图 5-40 polar 命令绘制图形 第 5 章 数据可视化 (2)在柱坐标系中绘图 【pol2cart 命令】 该命令用于将极坐标值或柱坐标值转换成直角坐标系下的坐标值,其转换规则如图 5-41 所示。然后使用 plot3、mesh 等命令绘图,即在直角坐标系下绘制使用柱坐标值描述的图形。 调用格式如下: [X,Y] = pol2cart(THETA,RHO) [X,Y,Z] = pol2cart(THETA,RHO,Z) 图 5-41 极坐标和柱坐标与直角坐标转换的规则 【示例】 >>theta=0:pi/20:2*pi; >>rho=sin(theta); >>[t,r]=meshgrid(theta,rho); >>z=r.*t; >>[X,Y,Z]=pol2cart(t,r,z); >>mesh(X,Y,Z) 用 pol2cart 命令绘制出的图形如图 5-42 所示。 图 5-42 pol2cart 命令绘制图形 (3)在球坐标系中绘图 【sph2cart 命令】 该命令用于将球坐标值转换成直角坐标系下的坐标值,其转换规则如图 5-43 所示。然后 使用 plot3、mesh 等命令绘图,即在直角坐标系下绘制使用球坐标值描述的图形。调用格式 如下: [x,y,z] = sph2cart(THETA,PHI,R) –151– MATLAB 7.0 从入门到精通 图 5-43 柱坐标与直角坐标转换的规则 (4)不同坐标系之间的转换 【cart2pol 命令】 该命令用于将直角坐标系下的坐标值转换成极坐标系和柱坐标系下的坐标值,转换规则 如图 5-41 所示。其调用格式如下: [THETA,RHO,Z] = cart2pol(X,Y,Z) [THETA,RHO] = cart2pol(X,Y) 【cart2sph 命令】 该命令用于将直角坐标系下的坐标值转换成球坐标系下的坐标值,转换规则如图 5-43 所 示。其调用格式如下: [THETA,PHI,R] = cart2sph(X,Y,Z) 5.3.4 四维表现图 对于三维图形,可以利用 z=z(x,y)的确定或不确定的函数关系来绘制图形,但此时 自变量只有两个,也即二维的。当自变量有 3 个时,定义域是整个三维空间,而我们所处的 空间和思维局限性,在计算机的屏幕上只能表现出三个空间变量,因此再没有什么空间变量 来表示函数的值了。为此,MATLAB 通过颜色来表示这存在于第四维空间的值,它由函数 slice 实现。其调用格式如下: slice(V,sx,sy,sz) //显示三元函数 V=V(X,Y,Z)确定的超立体形在 x 轴、y 轴与 z 轴方向上的若干点(对 应若干平面)的切片图,各点的坐标由数量向量 sx、sy 与 sz 指定。其中 V 为三维数组(阶数为 m*n*p), 默认的有:X = 1:m、Y = 1:n、Z = 1:p slice(X,Y,Z,V,sx,sy,sz) //显示三元函数 V=V(X,Y,Z)确定的超立体形在 x 轴、y 轴与 z 轴方向上的若干点 (对应若干平面)。即若函数 V=V(X,Y,Z)中有一变量如 X 取一定值 X0,则函数 V=V(X0,Y,Z)变成一立体曲面 (只不过是将该曲面通过颜色表示高度 V,从而显示于一平面而已)的切片图,各点的坐标由参量向量 sx、sy 与 sz 指定。参量 X、参量 Y 与参量 Z 为三维数组,用于指定立方体 V 的坐标。参量 X、Y 与 Z 必须有单调 的、正交的间隔(如同用命令 meshgrid 生成的一样)。在每一点上的颜色由对超立体 V 的三维内插值确定 slice(V,XI,YI,ZI) //显示由参量矩阵 XI、YI 与 ZI 确定的超立体图形的切面图。参量 XI、YI 与 ZI 定义 了一个曲面,同时会在曲面的点上计算超立体 V 的值。参量 XI、YI 与 ZI 必须为同型矩阵 slice(X,Y,Z,V,XI,YI,ZI) //沿着由矩阵 XI、YI 与 ZI 定义的曲面画穿过超立体图形 V 的切片 slice(...,’method’) //指定内插值的方法。’method’为如下方法之一:’linear’、’cubic’、’nearest’:’linear’—— 指定使用三次线性内插值法(该状态为默认的);’cubic’——指定使用三次立方内插值法;’nearest’——指定 使用最近点内插值法 slice(axes_handle,...) h = slice(...) //返回一曲面图形对象的句柄向量 h –152– 第 5 章 数据可视化 【示例】 在-2≤x≤2,-2≤y≤2,-2≤z≤2 区域内可视化函数 v = xe(−x2 −y2 −z2 ) 。 >>[x,y,z] = meshgrid(-2:.2:2,-2:.25:2,-2:.16:2); >>v = x.*exp(-x.^2-y.^2-z.^2); >>xslice = [-1.2,.8,2]; yslice = 2; zslice = [-2,0]; >>slice(x,y,z,v,xslice,yslice,zslice) >>colormap hsv 用 silice 命令绘制出的切片图如图 5-44 所示。 图 5-44 silice 绘制切片图 若要显示颜色和所代表数值(在上例中即指 V 的函数值)的关系,可以通过 colorbar 函 数命令来显示。 5.4 图形处理 MATLAB 7.0 除了提供强大的绘图功能外,它还提供了强大的图形处理的功能。下面对 这些相关的技术进行具体的介绍。 5.4.1 图形标注 从前面的示例图形可以看出,在绘制图形时,系统会自动为图形进行简单的标注。 MATLAB 语言还提供了丰富的图形标注函数供用户自由标注所绘的图形。 1.坐标轴标注和图形标题 对坐标轴进行标注和给图形填加标题的函数主要有 xlabel、ylabel、zlabel 和 title 等,它 们的调用格式基本相同,具体代码如下: xlabel(’string’) xlabel(...,’PropertyName’,PropertyValue,...) xlabel(fname) ylabel(’string’) ylabel(fname) zlabel(’string’) –153– MATLAB 7.0 从入门到精通 zlabel(fname) title(’string’) title(...,’PropertyName’,PropertyValue,...) title(fname) 其中 string 是标注所用的说明语句,fname 是一个函数名,系统要求该函数必须返回一 个字符串作为标注语句。’PropertyName’,PropertyValue 用于定义相应标注文本的属性和属性 值,包括字体大小、字体名和字体粗细等。 【示例】 >>x=1:0.1*pi:2*pi; >>y=sin(x); >>plot(x,y) >>xlabel(’x(0-2\pi)’,’fontweight’,’bold’); >>ylabel(’y=sin(x)’,’fontweight’,’bold’); >>title(’正弦函数’,’fontsize’,12,’fontweight’,’bold’,’fontname’,’隶书’) 通过上述示例代码实现对坐标轴和图形的标注,如图 5-45 所示。 y=sin(x) 正弦函数 1 0.8 0.6 0.4 0.2 0 -0.2 -0.4 -0.6 -0.8 -1 1 2 3 4 5 6 7 x(0-2π ) 图 5-45 坐标轴标注示例 在标注过程中经常会遇到特殊符号的输入问题,如上例中 pi 的输入,MATLAB 语言提 供了相应的字符转换。常见的转换如表 5-4 所示。 表 5-4 MATLAB7.0 中常见的字符转换 控制字符串 转换字符串 控制字符串 \alpha α \lambda \beta β \mu \gamma γ \xi \delta δ \pi \epsilon ε \omega \zeta ζ \tau \eta η \sigma \theta θ \kappa \leftarrow ← \uparrow 用户还可以对文本标注文字进行显示控制,具体方式如下: 转换字符串 λ μ ξ π ω τ ∑ κ ↑ –154– • \bf:黑体; • \it:斜体; • \sl:透视。 • \rm:标准形式; • \fontname{fontname}:定义标注文字的字体; • \fontsize{fontsize}:定义标注文字的字体大小。 第 5 章 数据可视化 2.用文本标注图形 在 MATLAB 语言中可以使用 text 或 gtext 命令对图形进行文本注释。使用 text 进行标注 时需要定义用于注释的文本字符串和放置注释的位置,而使用 gtext 命令进行标注却可以使用 鼠标来选择标注文字放置的位置。调用格式如下: text(x,y,’string’) text(x,y,z,’string’) text(...’PropertyName’,PropertyValue...) gtext(’string’) gtext({’string1’,’string2’,’string3’,...}) gtext({’string1’;’string2’;’string3’;...}) 在定义标注放置的位置可以通过函数的计算值来确定,而且标注中还可以实时地调用返 回值为字符串的函数,如 num2str 等,利用这些函数可以完成较为复杂的文本标注。 【示例】 >>x=1:0.1*pi:2*pi; >>y=sin(x); >>plot(x,y) >>xlabel(’x(0-2\pi)’,’fontweight’,’bold’); >>ylabel(’y=sin(x)’,’fontweight’,’bold’); >>title(’正弦函数’,’fontsize’,12,’fontweight’,’bold’,’fontname’,’隶书’) >>text(3*pi/4,sin(3*pi/4),’\leftarrowsin(t) = .707’,’FontSize’,16) >>text(pi,sin(pi),’\leftarrowsin(t) = 0’,’FontSize’,16) >>text(5*pi/4,sin(5*pi/4),’sin(t) = -.707\rightarrow’,’HorizontalAlignment’,’right’,’FontSize’,16) 用 text 命令标注的图形如图 5-46 所示。 y=sin(x) 正弦函数 1 0.8 ←sin(t) = .707 0.6 0.4 0.2 0 ←sin(t) = 0 -0.2 -0.4 -0.6 sin(t) = -.707→ -0.8 -1 1 2 3 4 5 6 7 x(0-2π ) 图 5-46 text 命令标注图形 –155– MATLAB 7.0 从入门到精通 【示例】 >>X=1:0.1*pi:2*pi; >>Y=sin(X); >>plot(X,Y) >>gtext(’y=sin(x) ’, ’fontsize’,12) 执行 gtext 命令后出现“+”形,如图 5-47 所示。执行后的结果如图 5-48 所示。 图 5-47 执行 gtext 命令后出现“+”型 1 0.8 0.6 0.4 y=sin(x) 0.2 0 -0.2 -0.4 -0.6 -0.8 -1 1 2 3 4 5 6 7 图 5-48 gtext 命令执行结果 除了上面示例中的一些标注功能外,用户还可以控制标注的对齐方式和添加多行标注文 字。下面通过一个示例来说明这些功能的效果,具体的控制方法请查阅帮助。 【示例】 >>Z = peaks; >>h = plot(Z(:,33)); >>x = get(h,’XData’); % Get the plotted data >>y = get(h,’YData’); >>imin = find(min(y) == y);% Find the index of the min and max >>imax = find(max(y) == y); >>text(x(imin),y(imin),[’Minimum=’,num2str(y(imin))],’VerticalAlignment’,’middle’,’HorizontalAlignment’,’lef t’,’FontSize’,14) >>text(x(imax),y(imax),[’Maximum=’,num2str(y(imax))],’VerticalAlignment’,’bottom’,’HorizontalAlignment’,’r ight’,’FontSize’,14) >>str1(1) = {’Center each line in the Uicontrol’}; –156– 第 5 章 数据可视化 >>str1(2) = {’Also check out the textwrap function’}; >>str2(1) = {’Each cell is a quoted string’}; >>str2(2) = {’You can specify how the string is aligned’}; >>str2(3) = {’You can use LaTeX symbols like \pi \chi \Xi’}; >>str2(4) = {’\bfOr use bold \rm\itor italic font\rm’}; >>str2(5) = {’\fontname{courier}Or even change fonts’}; >>uicontrol(’Style’,’text’,’Position’,[80 80 200 30],’String’,str1); >>text(45,0,str2,’HorizontalAlignment’,’right’) 通过上述示例代码来控制标注的对齐方式和添加多行标注文字,其效果如图 5-49 所示。 图 5-49 复杂的文本标注示例 3.图例标注 在对数值结果进行绘图时,经常会出现在一张图中绘制多条曲线的情况,这时用户可以 使用 legend 命令为曲线填加图例以便区分它们。该函数能够为图形中所有的曲线进行自动标 注,并以输入变量作为标注文本。其调用格式如下: legend(’string1’,’string2’,...) legend(...,’Location’,location) 其 中 ’string1’,’string2’等 分 别 标 注 对 应 绘 图 过 程 中 按 绘 制 先 后 顺 序 所 生 成 的 曲 线 , ’Location’,location 用于定义标注放置的位置。Location 可以是一个 1×4 向量([left bottom width height])或任意一个字符串之一。对图例位置标注定义如表 5-5 所示。 表 5-5 字符串 North East NorthEast SouthEast NorthOutside EastOutside NorthEastOutside SouthEastOutside Best 图例位置标注定义 位置 字符串 绘图区内的上中部 South 绘图区内的右部 West 绘图区内的右上部 NorthWest 绘图区内的右下部 SouthWest 绘图区外的上中部 SouthOutside 绘图区外的右部 WestOutside 绘图区外的右上部 NorthWestOutside 绘图区外的右下部 SouthWestOutside 标注与图形的重叠最小处 BestOutside 位置 绘图区内的底部 绘图区内的左中部 绘图区内的左上部 绘图区内的左下部 绘图区外的下部 绘图区外的左部 绘图区外的左上部 绘图区外的左下部 绘图区外占用最小面积 –157– MATLAB 7.0 从入门到精通 标注的位置还可以通过定位代号来定义,代号的说明如下: • 0:自动定位,使得标注图标与图形重叠最少; • 1:默认值,置于图形的右上角; • 2:置于图形的左上角; • 3:置于图形的左下角; • 4:置于图形的右下角; • -1:置于图形的右外侧。 用户还可以通过鼠标来调整图例标注的位置。 【示例】 >>x = -pi:pi/20:pi; >>plot(x,cos(x),’-ro’,x,sin(x),’-.b’) >>h = legend(’cos’,’sin’,2); 通过上述示例代码可以用鼠标来调整图例标注的位置,图例标注效果如图 5-50 所示。 1 0.8 cos 0.6 sin 0.4 0.2 0 -0.2 -0.4 -0.6 -0.8 -1 -4 -3 -2 -1 0 1 2 3 4 图 5-50 图例标注 5.4.2 坐标轴的控制 在 MATLAB 7.0 中可以通过设置各种参数来实现对坐标轴的控制,其中的高级控制涉及 到图形句柄,这里只介绍初级的控制命令。 1.axis 函数控制坐标轴特征 该命令用于控制坐标轴的刻度范围及显示形式,调用格式如下: axis([xmin xmax ymin ymax]) axis([xmin xmax ymin ymax zmin zmax cmin cmax]) axis ’控制字符串’ 其中[xmin xmax ymin ymax zmin zmax]用于定义坐标轴的范围,控制字符串可以是表 5-6 中的任一表达式。 表 5-6 axis 命令的控制字符串 字符串 说明 auto 自动模式,使得坐标轴范围能容纳下所有的图形 manual 以当前的坐标范围限定图形的绘制,此后使用 hold on 命令再次绘图时保持坐标轴范围不变 tight 将坐标范围限制在指定的数据范围内 –158– 字符串 说明 fill 设置坐标范围和 PlotBoxAspectRatio 属性以使坐标满足要求 ij 将坐标设置成矩阵形式,原点在左上角 xy 将坐标设置成直角坐标系 equal 将各坐标轴的刻度设置成相同 image 与 equal 类似 square 设置绘图区为正方形 vis3d 使图形在旋转或拉伸过程中保持坐标轴的比例不变 normal 解除对坐标轴的任何限制 off 取消对坐标轴的一切设置 on 恢复对坐标轴的一切设置 【示例】 x = 0:.025:pi/2; plot(x,tan(x),’-ro’) 使用 axis 命令设定坐标轴之前的图如图 5-51 所示。 50 45 40 35 30 25 20 15 10 5 0 0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 图 5-51 使用 axis 设定坐标轴之前的图 axis([0 pi/2 0 5]) 使用 axis 命令设定坐标轴之后的图如图 5-51 所示。 5 4.5 4 3.5 3 2.5 2 1.5 1 0.5 0 0 0.5 1 1.5 图 5-52 使用 axis 设定坐标轴之后的图 第 5 章 数据可视化 续表 –159– MATLAB 7.0 从入门到精通 2.zoom 函数控制坐标轴缩放 该命令用于实现对二维图形的缩放,其调用格式如下: zoom ’控制字符串’ 其中控制字符串可以是表 5-7 中的任一字符串。 表 5-7 字符串 空 (factor) on off out reset xon yon zoom 命令的控制字符串 说明 在 zoom on 和 zoom off 之间切换 以 factor 作为缩放因子进行坐标轴的缩放 允许对坐标轴进行缩放 禁止对坐标轴进行缩放 恢复到最初的坐标轴设置 设置当前的坐标轴为最初值 允许对 x 轴进行缩放 允许对 y 轴进行缩放 3.grid 函数控制坐标轴网格 该命令用于绘制坐标网格,其调用格式如下: grid on //给当前坐标轴填加网格线 grid off //取消当前坐标轴的网格线 grid minor //设置网格绘制的密度,即网格线间的间距 grid //在 grid on 和 grid off 之间切换 【示例】 >>X=0:0.1*pi:2*pi; >>Y=sin(X); >>plot(X,Y); >>grid on 使用 grid 函数添加网格线,如图 5-53 所示。 1 0.8 0.6 0.4 0.2 0 -0.2 -0.4 -0.6 -0.8 -1 0 1 2 3 4 5 6 7 图 5-53 添加网格线 –160– 4.box 函数控制坐标轴封闭 第 5 章 数据可视化 该命令用于在图形四周都能显示坐标,其调用格式如下: box on box off box 【示例】 对 grid 一节中的示例添加“>>box off”命令,结果如图 5.54 所示,注意观察与上例图形 的区别。 1 0.8 0.6 0.4 0.2 0 -0.2 -0.4 -0.6 -0.8 -1 0 1 2 3 4 5 6 7 图 5-54 box 命令示例 5.4.3 图形数据取点 当用户为某一函数作好图形后,有时候会希望知道在某一自变量值下的函数值,这时使 用取点命令 ginput 可方便地通过鼠标来读取二维平面图中任一点的坐标值。其调用格式如下: [x,y] = ginput(n) //用户可以通过鼠标选择 n 个点,它们的坐标值保存在[x,y]中,用户可通过 enter 键来 结束取点; [x,y] = ginput //取点的数目不受限制,它们的坐标值保存在[x,y]中,用户通过 enter 键来结束取点; [x,y,button] = ginput(...) //返回值 button 记录了在选取每个点时的相关信息; 【示例】 >>x=0:0.1*pi:2*pi; >>y=sin(x); >>plot(x,y) >>[m n]=ginput(1) >>hold on >>plot(m,n,’or’) >>text(m(1),n(1),[’m(1)=’,num2str(m(1)),’n(1)=’,num2str(n(1))]) 用户在通过鼠标取点前会出现“+”形,如图 5-55 所示。用 ginput 命令执行的结果如图 5-56 所示。 –161– MATLAB 7.0 从入门到精通 图 5-55 取点确定前出现“+”形 1 0.8 0.6 0.4 0.2 m(1)=2.9113n(1)=0.2193 0 -0.2 -0.4 -0.6 -0.8 -1 0 1 2 3 4 5 6 7 图 5-56 ginput 命令执行结果 取点结束后,在 MATLAB 7.0 命令窗口显示以下结果: m= 3.1855 n= -0.0322 5.4.4 子图和图形保持 在绘图过程中,用户会经常碰到下面两种情况。在已存的一张图中填加新的曲线;将几 个图形在同一个图形窗口中表现出来,但又不在同一个坐标系中绘制。MATLAB 7.0 为这两 种需要分别提供 hold 函数和 subplot 函数,前面的示例已用到 hold 函数,下面对它们的使用 方法进行介绍。 【hold 命令】 该命令常用调用格式如下: hold on //启动图形保持功能,此后绘制的图形都将填加到当前的图形窗口中,并自动调整坐标轴范围 hold off //关闭图形保持功能 hold //在 hold on 和 hold off 之间切换 在前面的示例中已经使用到该命令,使用方法比较简单,用户只要实践几次就可掌握, 在此不再举例说明。 【subplot 命令】 该命令用于生成并控制多个坐标轴,绘制出的效果图如图 5-57 所示。把当前图形窗口分 –162– 第 5 章 数据可视化 隔成几个矩形部分,不同的部分是按行方向以数字进行标号的。每一部分有一坐标轴,后面 的图形输出在当前的部分中。常用调用格式如下: subplot(m,n,p) //将一图形窗口分成 m*n 个小窗口,在第 p 个小窗口中创建一坐标轴。则新的坐标轴成 为当前坐标轴。若 p 为一向量,则创建一坐标轴,包含所有罗列在 p 中的小窗口。用户可以通过参数 p 分别 对各子绘图区域进行操作,子绘图区域的编号按行从左至右编号,如图 5-58 所示。 subplot(m,n,p,’replace’) //如果定义的坐标轴已存在,那么就删掉已有的,并创建一个新的坐标轴 subplot(m,n,p,’align’) //对齐坐标轴 subplot(h) //使句柄 h 对应的坐标轴称为当前的,用于后面图形的输出显示 subplot(’Position’,[left bottom width height]) //在由 4 个元素指定的位置上创建一坐标轴。位置元素的单 位为归一化单位 【示例】 >>income = [3.2 4.1 5.0 5.6]; >>outgo = [2.5 4.0 3.35 4.9]; >>subplot(2,1,1); plot(income) >>subplot(2,1,2); plot(outgo) 6 5 4 3 1 1.5 2 2.5 3 3.5 4 5 4.5 4 3.5 3 2.5 1 1.5 2 2.5 3 3.5 4 【示例】 >>subplot(2,2,[1 3]) >>subplot(2,2,2) >>subplot(2,2,4) 图 5-57 subplot 命令作图效果 1 1 0.9 0.8 0.6 0.8 0.4 0.7 0.2 0.6 0 0 0.5 1 0.5 0.4 1 0.8 0.3 0.6 0.2 0.4 0.1 0.2 0 0 0 0.5 1 0 0.5 1 图 5-58 subplot 的 p 参数为向量 –163– MATLAB 7.0 从入门到精通 5.4.5 色彩控制 图形的一个重要因素就是图形的颜色,丰富的颜色变化能让图形更具表现力。在 MATLAB 7.0 中,色图 colormap 是完成这方面工作的主要命令。 MATLAB 是采用颜色映像来处理图形颜色的,即 RGB 色系。计算机中的各种颜色都是 通过三原色按不同比例调制出来的,三原色即红(Red)、绿(Green)、蓝(Blue)。每一种颜 色的值表达为一个 1×3 的向量[R G B],其中 R、G、B 值的大小分别代表这 3 种颜色之间的 相对亮度,因此它们的取值范围均必须在[0,1]区间内。每一种不同的颜色对应一个不同的 向量。表 5-8 给出了典型的颜色配比方案。 表 5-8 典型的配色方案 原色 调得的颜色 红(R) 绿(G) 蓝(B) 0 0 0 黑色 1 1 1 白色 1 0 0 红色 0 1 0 绿色 0 0 1 蓝色 1 1 0 黄色 1 0 1 洋红色 0 1 1 青色 0.5 0.5 0.5 灰色 调好颜色后,就可以用它作为绘图用色。一般的线图函数(如 plot,plot3 等)不需要色图 来控制其色彩显示,而对于面图函数(如 mesh,surf 等)则需要调用色图。色图设定命令为: colormap([R,G,B]) 其中,输入变量[R,G,B]为一个三列矩阵,行数不限,该矩阵称为色图。表 5-9 给出 了几种典型的常用色图的名称和其产生函数。 表 5-9 红黄色图 蓝色调灰度色图 青红浓淡色图 线性灰度色图 黑红黄白色图 饱和色图 粉红色图 光谱色图 线性色图 色图名称 常用色图的名称及其产生函数 autumn bone cool gray hot hsv pink prism lines 产生函数 –164– 第 5 章 数据可视化 【示例】 >>colormap(hsv(128)) 此语句是用于定义当前窗口的颜色映象为饱和色图,其颜色定义了 128 种。如果用户没 有定义颜色的多少,那么色图的大小(颜色种数的数目)与当前色图的大小相同。 【示例】 >>[x,y,z]=peaks; >>mesh(x,y,z); >>colormap(autumn (128)) 使用 autumn 命令绘制的图如图 5-59 所示。 图 5-59 使用 autumn 色图绘图 除了 colormap 命令外,用户还可以通过以下一些常用函数命令对色图进行控制。 【brighten 函数命令】 该函数用于增亮或变暗色图。常见调用格式如下: brighten(beta) //增亮或变暗当前的色图。若 0>.surf(peaks(30)) >>colorbar 通过 colorbar 显示颜色标尺,如图 5-60 所示。 –165– MATLAB 7.0 从入门到精通 图 5-60 颜色标尺 【rgbplot 函数命令】 该函数用于画出色图。常见调用格式如下: rgbplot(cmap) //画出维数为 m*3 的色图矩阵 cmap 的每一列,矩阵的第一列为红色强度,第二列为绿 色强度,第三列为蓝色强度 【示例】 >>rgbplot(copper) 用 rgbplot 命令绘制出的色图如图 5-61 所示。 1 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0 0 10 20 30 40 50 60 70 图 5-61 rgbplot 命令画出色图 【caxis 函数命令】 该函数用于颜色轴刻度,控制数值与色彩间的对应关系以及颜色的显示范围。常见调用 格式如下: caxis([cmin cmax]) //在[cmin cmax]范围内与色图的色值相对应,并依此为图形着色。若数据点的值小 于 cmin 或大于 cmax,则按等于 cmin 或 cmax 来进行着色 caxis auto //MATLAB 自动计算出色值的范围 caxis manual //按照当前的色值范围设置色图范围 caxis(caxis) //与 caxis manual 实现相同的功能 v = caxis //返回当前色图的范围的最大和最小值[cmin cmax] 【示例】 >>.surf(peaks(30)) >>colorbar >>caxis([-2 2]) 用 caxis 命令绘制出的图形如图 5-62 所示。 –166– 第 5 章 数据可视化 图 5-62 caxis 命令绘图效果 【shading 函数命令】 该函数用于控制曲面图形的着色方式。常见调用格式如下: shading flat //平滑方式着色 shading faceted //以平面为着色单位,系统默认的着色方式 shading interp //以插值形式为图形的像点着色 【示例】 >>subplot(3,1,1); sphere(16) >>axis square; shading flat >>title(’Flat Shading’) >>subplot(3,1,2); sphere(16) >>axis square; shading faceted >>title(’Faceted Shading’) >>subplot(3,1,3); sphere(16) >>axis square; shading interp >>title(’Interpolated Shading’) 用 shading 命令控制图形着色法方式如图 5-63 所示。 图 5-63 控制图形着色方式 【colordef 函数命令】 该函数用于设置图形的背景颜色。常见调用格式如下: colordef white //将图形背景颜色设置为白色 –167– MATLAB 7.0 从入门到精通 colordef black //将图形背景颜色设置为黑色 colordef none //将图形背景和图形窗口颜色设置成 MATLAB 系统的默认色彩,MATLAB7 中为黑色 colordef(fig,color_option) //将图形句柄为 fig 的图形的背景设置为由 color_option 定义的颜色 h = colordef(’new’,color_option) 【示例】 >>colordef none >>surf(peaks(30)) 用 colordef 函数命令将图形背景色设为默认值。 图 5-64 设置图形背景色为默认值 >>colordef black >>surf(peaks(30)) 用 colordef 函数命令将图形背景色设为黑色。 图 5-65 设置图形背景色为黑色 5.4.6 视角与光照 三维视图表现的是一个空间内的图形,因此从不同的位置和角度观察图形会有不同的效 果。MATLAB 提供对图形进行视觉及光源控制的功能。所谓视觉就是图形展现给用户的角度, 而光照就是图形色彩强弱变化的方向。下面向读者介绍与视角和光照相关的 MATLAB 命令。 1.视角控制命令 MATLAB 语言中用于视觉控制的命令函数主要有 view、viewmtx 和 rotate3D,下面分别 –168– 第 5 章 数据可视化 介绍它们的调用方法。 【view 函数命令】 该命令用于指定立体图形的观察点。观察者(观察点)的位置决定了坐标轴的方向。用 户可以用方位角(azimuth)和仰角(elevation)一起,或者用空间中的一点来确定观察点的 位置,如图 5-66 所示。其调用格式如下: 图 5-66 仰角和方位角示意图 view(az,el)、view([az,el]) //为三维空间图形设置观察点的方位角。方位角 az 与仰角 el 是按下面的方法 定义的两个旋转角度:作一通过视点与 z-轴的平面,与 xy 平面有一交线,该交线与 y-轴的反方向的、按逆 时针方向(从 z-轴的方向观察)计算、单位为度的夹角,就是观察点的方位角 az。若角度为负值,则按顺时 针方向计算;在通过视点与 z-轴的平面上,用一直线连接视点与坐标原点,该直线与 xy 平面的夹角就是观 察点的仰角 el。若仰角为负值,则观察点转移到曲面下面 view([x,y,z]) //在笛卡尔坐标系中将视角设为沿向量[x,y,z]指向原点,例如 view([0 0 1])=view(0,90),也 即在笛卡尔坐标系中将点(x,y,z)设置为视点。注意输入参量只能是方括号的向量形式,而非数学中的点的 形式 view(2) //设置默认的二维形式视点。其中 az=0,el=90,即从 z-轴上方观看所绘图形 view(3) //设置默认的三维形式视点。其中 az=-37.5,el=30 view(T) //根据转换矩阵 T 设置视点。其中 T 为 4*4 阶的矩阵,如同用命令 viewmtx 生成的透视转换 矩阵一样 [az,el] = view //返回当前的方位角 az 与仰角 el T = view //返回当前的 4*4 阶的转换矩阵 T 【示例】 >>X=0:0.1*pi:2*pi; Z=sin(X); Y=zeros(size(X)) ; >>subplot(2,2,1) >>plot3(X,Y,Z,’b’); grid; >>xlabel( ’X-axis ’ ); ylabel( ’Y-axis ’ ); zlabel(’ Z-axis ’); >>title( ’ Default Az = -37.5,E1 = 30 ’); >>view(-37.5,30); >>subplot(2,2,2) >>plot3(X,Y,Z); grid; >>xlabel( ’X-axis ’); ylabel( ’Y-axis ’); zlabel( ’Z-axis ’ ) >>title( ’Az Rotated to 52.5 ’ ) >>view(-37.5+90,30) –169– MATLAB 7.0 从入门到精通 >>subplot(2,2,3) >>plot3(X,Y,Z,’r’); grid; >>xlabel( ’X-axis ’ ); ylabel( ’Y-axis ’ ); zlabel( ’Z-axis ’); >>title( ’ E1 Increased to 60 ’ ) >>view(-37.5,60) >>subplot(2,2,4) >>plot3(X,Y,Z,’g’); grid; >>xlabel( ’X-axis ’); ylabel( ’Y-axis ’); zlabel( ’Z-axis ’) >>title( ’Az = 0,E1 = 90 ’ ) >>view(0,90) 用 view 函数命令设置视点,效果如图 5-67 所示。 Z-axis Default Az = -37.5,E1 = 30 Az Rotated to 52.5 1 1 Z-axis 0 0 -1 1 0 Y-axis -1 0 10 5 X-axis E1 Increased to 60 -1 0 5 X-axis 10 -1 1 0 Y-axis Az = 0,E1 = 90 1 1 0.5 Y-axis 0 0 -1 1 10 -0.5 0 Y-axis -1 0 5 X-axis -1 0 2 4 6 8 X-axis Z-axis 图 5-67 为图形设置视点 【viewmtx 函数命令】 该命令用于视点转换矩阵。计算一个 4*4 阶的正交的或透视的转换矩阵,该矩阵将一四 维的、齐次的向量转换到一个二维的视平面上。其调用格式如下: T = viewmtx(az,el) //返回一个与视点的方位角 az 与仰角 el(单位都为度)对应的正交矩阵,并没有改 变当前视点 T = viewmtx(az,el,phi) //返回一个透视的转换矩阵,其中参量 phi 是单位为度的透视角度,为标准化立 方体(单位为度)的对象视角角度与透视扭曲程度。Phi 的取值如表 5-10 所示。用户可以通过使用返回的矩 阵,用命令 view(T)改变视点的位置 T = viewmtx(az,el,phi,xc) 返回以在标准化的图形立方体中的点 xc 为目标点的透视矩阵(就像相机正对 着点 xc 一样),目标点 xc 为视角的中心点。用户可以用一个三维向量 xc=[xc,yc,zc]指定该中心点,每一分量 都在区间[0,1]上。默认值为 xc=[0 0 0] 表 5-10 Phi 的值 0度 10 度 25 度 60 度 phi 的取值 正交投影 类似以远距离投影 类似于普通投影 类似以广角投影 说明 –170– 【示例】 >>x = [0 1 1 0 0 0 1 1 0 0 1 1 1 1 0 0]; >>y = [0 0 1 1 0 0 0 1 1 0 0 0 1 1 1 1]; >>z = [0 0 0 0 0 1 1 1 1 1 1 0 0 1 1 0]; >>A = viewmtx(-37.5,30); >>[m,n] = size(x); >>x4d = [x(:),y(:),z(:),ones(m*n,1)]’; >>x2d = A*x4d; >>x2 = zeros(m,n); y2 = zeros(m,n); >>x2(:) = x2d(1,:); >>y2(:) = x2d(2,:); >>plot(x2,y2) 用 viewmtx 函数绘制出的效果图如图 5-68 所示。 第 5 章 数据可视化 1.6 1.4 1.2 1 0.8 0.6 0.4 0.2 0 -0.8 -0.6 -0.4 -0.2 0 0.2 0.4 0.6 0.8 图 5-68 viewmtx 命令 【rotate3D 函数命令】 该命令为三维视角变化函数,它的使用将触发图形窗口的 rotate3D 选项,用户可以方便 地用鼠标来控制视角的变化,且视角的变化值也将实时地显示在图中。 【示例】 >>surf(peaks(20)); >>rotate3D 在图形窗口的图形区域内按住鼠标左键不放来调节视角,用户可以很方便地从不同的角 度来观察所绘得的图形,效果如图 5-69 所示。 图 5-69 旋转三维图形 –171– MATLAB 7.0 从入门到精通 2.三维透视命令 在 MATLAB7.0 中使用 mesh 等命令绘制网格曲面时,系统在默认情况下会隐藏重叠在后 面的网格,利用透视命令 hidden 可以看到被遮的部分。其调用格式如下: hidden on hidden off 【示例】 >>mesh(peaks) >>hidden off 用三维透视命令绘制完成的图形如图 5-70 所示。 图 5-70 三维透视图形 3.光照控制命令 MATLAB 语言提供了如表 5-11 所述的光照控制命令。本小节只介绍其中几个常用的函 数命令,其他命令的用法请读者查阅帮助。 表 5-11 MATLAB 语言中的图形光照控制命令 函数名 说明 函数名 说明 light 设置曲面光源 specular 镜面反射模式 surfl 绘制存在光源的三维曲面图 diffuse 漫反射模式 lighting 设置曲面光源模式 lightangle 球坐标系中的光源 material 设置图形表面对光照反映模式 【light 函数命令】 该命令为当前图形建立光源。其主要的调用格式如下: light(’PropertyName’,PropertyValue,...) // PropertyName 属性名是一些用于定义光源的颜色、位置和类型 等的变量名,更为详细的介绍请查阅帮助 handle = light(...) //返回光源对象的句柄 【示例】 >>h = surf(peaks); >>light(’Position’,[1 0 0],’Style’,’infinite’); 用 light 命令为图形设置光源,如图 5-71 所示。 –172– 第 5 章 数据可视化 图 5-71 为图形设置光源 【lighting 函数命令】 该命令用于设置曲面光源模式,其调用格式如下: lighting flat //平面模式,以网格为光照的基本单元。这是系统默认的模式 lighting gouraud //点模式,以像素为光照的基本单元 lighting phong //以像素为光照的基本单元,并计算考虑了各点的反射 lighting none //关闭光源 【示例】 >>subplot(2,2,1) >>mesh(peaks);light(’Position’,[1 0 0],’Style’,’infinite’); >>lighting phong >>subplot(2,2,2) >>mesh(peaks);light(’Position’,[1 0 0],’Style’,’infinite’); >>lighting flat >>subplot(2,2,3) >>mesh(peaks);light(’Position’,[1 0 0],’Style’,’infinite’); >>lighting none >>subplot(2,2,4) >>mesh(peaks);light(’Position’,[1 0 0],’Style’,’infinite’); >>lighting gouraud 用 lighting 命令设置曲面光源模式,如图 5-72 所示。 图 5-72 设置曲面光源模式 –173– MATLAB 7.0 从入门到精通 【material 函数命令】 该命令用于设置图形表面对光照反映模式,其调用格式如下: material shiny //图形表面显示较为光亮的色彩模式 material dull //表面显示较为阴暗的色彩模式 material metal //表面呈现金属光泽的模式 material([ka kd ks]) //[ka kd ks]用于定义图形的 ambient/diffuse/specular 三种反射模式的强度 material([ka kd ks n]) // n 用于定义镜面反射的指数 material([ka kd ks n sc]) // sc 用于定义镜面反射的颜色 material default 【surfl 函数命令】 该命令用于绘制三维带光照模式的阴影图。surfl(…)效果与命令 surf(…)基本上一样,但 是它会受光源的影响。图形的色泽取决于曲面的漫反射、镜面反射与环境光照模式。其主要 调用格式如下: surfl(Z)、surfl(X,Y,Z)、surfl(Z,S)、surfl(X,Y,Z,S)、surfl(X,Y,Z,S,K) //这些都是有效的使用形式。参数 S 为一个三维向量[Sx,Sy,Sz],用于指定光源的方向。S 也可视为点坐标系下的二维向量[AZ,EL]。S 的默认值 为从当前观察方向逆时针旋转 45°。使用命令组 cla;hold on;view(AZ,EL);surfl(…);hold off 等可画出视 角方向为(AZ,EL)的带光照模式的曲面图形。第 5 个参数 K=[ka,kd,ks,spread]用于指定环境光、漫反射光、镜 面反射光、扩散系数等的强弱 surfl(…,’light’) //用 LIGHT 对象生成一带颜色和光照模式的曲面。该命令可以生成与用默认光照模式 效果不同的曲面 surfl(…,’cdata’) //指定的曲面的反射光的颜色为 cdata H = surfl(…) //返回曲面与光源的句柄 【示例】 >>[X,Y] = meshgrid(-3:1/8:3); >>Z = peaks(X,Y); >>surfl(X,Y,Z); >>shading interp; >>colormap(gray); 用 surfl 命令绘制的图形如图 5-73 所示。 –174– 图 5-73 surfl 函数绘图 5.4.7 图形的打印和输出 第 5 章 数据可视化 用户在使用各种绘图命令作出精美的图形后,经常需要将图形打印或是以图片形式放在 其他文档中保存或是在其他图片处理软件中作进一步的处理。MATLAB 为用户提供了 3 种不 同的方式输出当前的图形。首先是通过图形窗口的命令菜单或是工具栏中的打印选项来输出; 其次,使用 MATLAB 语言提供的内置打印引擎或系统的打印服务来实现;最后可以以其他 的图形格式存储图形,然后通过其他的图形处理软件对其进行处理和打印。 利用菜单或工具栏来实现打印非常简单,这也是用户最常用的一种方式,在这里不作详 细介绍。下面简单介绍实现打印的函数 print 的基本调用格式。代码设置如下: print //将图形发送到由 printopt 定义的打印设备和系统打印命令中 print filename //将图形输出到文件 filename,如果 filename 没有扩展名,print 命令将自动选择一个合 适的扩展名 print –ddriver //使用由 ddriver 定义的打印设备打印当前图形 print –dformat //将当前图形复制到系统粘贴板(仅对 Windows 适用),图片的格式可以是-dmeta (Windows Enhanced Metafile)或 dbitmap (Windows Bitmap). print -dformat filename //以用户自定义的图形格式 dformat 将图形输出到用户自定义的 filename 文件中 print –smodelname //打印当前 Simulink 模型 smodelname print ... –options //定义打印选项,关于各种可设置的选项名请读者查阅帮助 [pcmd,dev] = printopt //返回当前系统的打印命令到字符串变量 pcmd 和输出设备到 dev 字符串变量 5.5 图形窗口 用户通过前面介绍的绘图命令得到的图形都是在相同的图形窗口中绘制的,它们都具有 相同的窗口菜单和工具栏。这个窗口是 MATLAB 7.0 所有图形输出的专用窗口,在利用前面 的每一个绘图命令绘图时,这个窗口是随之自动生成的,所以用户到此为止可能并不知道这 个窗口是如何产生的,以及它有哪些作用。下面将向读者介绍如何在利用函数命令绘制图形 之前生成图形窗口,以及利用窗口中的菜单和工具栏命令对图形和图形窗口的属性进行各种 设置。 5.5.1 图形窗口的创建与控制 创建图形窗口的命令是 figure,其调用方式如下: figure //创建一个图形窗口对象 figure(’PropertyName’,PropertyValue,...) //按照用户自定义的属性创建一个图形窗口对象,用户可以为图 形窗口对象设置的属性和参数请查阅帮助 figure(h) //如果 h 句柄所对应的窗口对象已存在,则该命令使得该图形窗口成为当前窗口;如果不存 在,则新建一个句柄值为 h 的窗口对象 h = figure(...) //返回图形窗口对象的句柄 用户可以通过下面两个命令来分别查阅和设置图形窗口的属性和参数。 –175– MATLAB 7.0 从入门到精通 get(n) //返回句柄值为 h 的图形窗口的参数名称及其当前值 set(n) //返回可为句柄值为 h 的图形窗口的参数名称及其用户可为这些参数设置的值 【示例】 • 在 MATLAB7.0 命令窗口执行“>>figure(2)”命令,打开“Figure2”窗口,如图 5-74 所示。 图 5-74 创建图形窗口 注意:图形窗口的标题为 Figure 2,如果使用“>>figure”命令则标题为 Figure 1。 • 执行“>>get(2)”,则在命令窗口中出现下面的代码。 Alphamap = [ (1 by 64) double array] BackingStore = on CloseRequestFcn = closereq Color = [0.8 0.8 0.8] Colormap = [ (64 by 3) double array] ...... //为了减小篇幅删去了这一部分内容 UserData = [] Visible = on • 执行“>>set(2)”,则在命令窗口中出现下面的代码。 Alphamap BackingStore: [ {on} | off ] CloseRequestFcn: string -or- function handle -or- cell array Color Colormap ......//为了减小篇幅删去了这一部分内容 UIContextMenu UserData Visible: [ {on} | off ] 5.5.2 图形窗口的菜单操作 下面详细介绍图形窗口的各项菜单命令。 –176– 1.【File】菜单 第 5 章 数据可视化 该菜单与 Windows 系统的菜单类似,下面介绍其各个菜单项的功能。 【New】选项用于新建一个 M-文件(M-File)、图形窗口(Figure)、Simulink 模型(Model)、 MATLAB 7.0 工作空间的变量(Variable)或用户界面(GUI)。其中创建 GUI 设置的对话框 如图 5-75 所示。 图 5-75 创建 GUI 的设置对话框 【Generate M-File】选项用于生成 M-函数文件。 【示例】 • 在 MATLAB 7.0 命令窗口中执行“>>mesh(peaks)”命令,打开如图 5-76 所示的窗口。 图 5-76 绘制图形 • 在生成的图形窗口中选择“Generate M-File”选项,生成如图 5-77 所示的文件,保存 该文件。 图 5-77 生成 M-函数文件 –177– MATLAB 7.0 从入门到精通 • 在 MATLAB 命令窗口执行“>>createfigure(peaks)”命令可以得到与 mesh(peaks)命令 一样的图形(包括对图形的各种设置也是一样的),如图 5-76 所示。 【Import Data】选项用于导入数据。 【Save Workspace As】选项用于将图形窗口中的图形数据存储在二进制 mat 文件中,它们 可以供其他的编程语言(如 C 语言等)调用。 【Preferences】选项用于定义图形窗口的各种设置,包括字体、颜色等,如图 5-78 所示。 【Export Setup】选项用于打开“图形输出”对话框,可以把图形以 emf、ai、bmp、eps、 jpg、pdf 等格式保存,并设置有关图形窗口的显示等方面的参数,如图 5-79 所示。读者可以 选择不同的设置以观察图形窗口有什么变化。 图 5-78 “图形窗口设置”对话框 图 5-79 “图形输出”对话框 【Page Setup】选项用于打开“页面设置”对话框,如图 5-80 所示。设置图形尺寸、纸张 大小、线型及文本类型以及坐标轴和图形设置。 图 5-80 “页面设置”对话框 【Print Setup】选项用于打开“打印设置”对话框。在这里可以设置图片的题图等。 【Print Preview】选项用于打开打印预览对话框,如图 5-81 所示。 【Print】选项用于打开打印对话框。 –178– 第 5 章 数据可视化 图 5-81 “打印预览”对话框 2.【Edit】菜单 该菜单中各选项的主要功能如下。 【Copy Figure】选项用于复制图形。 【Copy Option】选项用于打开“复制设置”对话框,设置图形复制的格式、图形背景颜 色和图形大小等。该选项打开的对话框界面与“File→Preferences”的界面相同,只是当前显 示的面板不同。 【Figure Properties】选项用于打开图形窗口的属性设置对话框,如图 5-82 所示。 图 5-82 “图形窗口的属性设置”对话框 【Axes Properties】选项用于打开“设置坐标轴属性”对话框。 【Current Object Properties】选项用于打开设置图形窗口中当前对象(如窗口中的坐标轴、 图形等)属性的对话框。 【Colormap】选项用于打开“色图编辑”对话框,如图 5-83 所示。 –179– MATLAB 7.0 从入门到精通 图 5-83 “色图编辑”对话框 【Clear Figure】、【Clear Command Window】、【Clear Command History】和【Clear Workspace】 选项分别用于清除图形窗口中的图形、清除命令窗口、清除历史命令和清空工作空间。 3.【View】菜单 该菜单各选项主要用于打开各种工具栏和控制面板,选中该菜单的所有选项后得到如图 5-84 所示的图形窗口。 图 5-84 打开各种工具栏的图形窗口 (1)图形窗口工具条主要用于对图形进行各种处理(如旋转三维图形、取得图形中某点 的坐标值、插入图例标注和插入色图条等); (2)照相工具条主要用于设置图形的视角和光照等,用户可以通过它们来实现从不同角 度来观察所绘三维图形,并且为图形设置不同的光照情况; (3)绘图编辑工具条主要用于向图形中添加文本标注和各种标注图形等; (4)绘图浏览器用于浏览当前图形窗口中的所有图形对象。 读者只要对其中各种工具栏的各个工具图标加以试验,就不难发现前面介绍的许多处理 –180– 第 5 章 数据可视化 图形的命令其实可以直接通过这些直观的图标工具来实现,读者只要熟练掌握这些工具条的 应用,就可以完成大部分图形处理工作,而不需要去记忆大量的命令函数。 4.【Insert】菜单 该菜单主要用于向当前图形窗口中插入各种标注图形(如插入单箭头、文字等),即实 现绘图编辑工具条中的各种功能。 5.【Tools】菜单 该菜单中大部分选项实现的功能使用前面介绍的几个工具条的相关命令图标同样可以 实现,这里通过一个示例来重点介绍【Basic Fitting】和【Data Statistics】两菜单项的功能。 【示例】 • 在 MATLAB 7.0 命令窗口中执行“>>figure”命令,然后选中【View】菜单中所有的 选项,如图 5-85 所示。 图 5-85 “Figure”窗口 • 在 MATLAB 命令窗口中执行下面的命令,MATLAB 的工作空间中出现两个变量 x,y。 >>x=[0:0.1*pi:2*pi]; >>y=sin(x)+3; • 在图 5-85 中的【Figure Palette】面板中选择“2D Axes”,然后选择【Plot Browser】中 的“Axes(no title)”使该浏览器中的“Add Data”按钮变成可用,并单击该按钮。按如 图 5-86 所示设置 x,y 轴的数据。 –181– MATLAB 7.0 从入门到精通 图 5-86 为图形窗口添加坐标轴和数据 • 设置好绘图方式和 x,y 轴的数据变量后,单击图 5-86 中的“OK”按钮,就会绘制出 数据,如图 5-87 所示。 图 5-87 绘制图形 • 选中【Tools】菜单中的“Data Statistics”选项,出现如图 5-88 所示的数据统计对话框, 其中显示了 x,y 变量数据的最小值、最大值、平均值等。选中 x,y 列中的“mean” 选择框,观察绘图区的变化。 –182– 第 5 章 数据可视化 图 5-88 图形数据的统计 • 取消【View】菜单中的“Figure Palette”、“Plot Browser”和“Property Editor”选项, 选中【Tools】菜单中的“Basic Fitting”选项,然后选择右箭头按钮,并按如图 5-89 所示进行设置。图形窗口就会变成如图 5-90 所示的拟合曲线和拟合误差曲线。 图 5-89 设置图形数据拟合的参数 图 5-90 曲线拟合的结果 –183– MATLAB 7.0 从入门到精通 6.【Desktop】菜单 该菜单用于将窗口合并到 MATLAB 7.0 主界面的窗口中。 7.【Window】菜单和【Help】菜单 这两个菜单与 Windows 系统中各种应用程序的用户界面的相关菜单很类似,在此不作详 细介绍。 5.5.3 图形窗口的工具栏 在表 5-12 中对如图 5-85 所示的 figure 窗口中各工具栏的功能进行了较为详细的介绍。 表 5-12 图形窗口工具栏 工具栏图标 说明 工具栏图标 新建一个图形窗口 打开图形窗口文件(后缀.fig) 保存图形窗口文件 放大图形窗口中的图形 缩小图形窗口中的图形 插入图例 打开绘图工具 设置光照相关属性 在水平方向设置视角 设置视角大小 以 X 方向为标准设置环行视角 以 Z 方向为标准设置环行视角 重置图形的视角和光照 设置绘图颜色 填加下画线 粗体 左对齐 插入直线 插入双箭头 插入文本框 插入椭圆 对齐 【示例】 • 在命令窗口执行下面的命令: >>plot([0:0.1:12],sin([0:0.1:12])) hold on 说明 打印图形 移动图形 旋转三维图形 取点 插入颜色条 隐藏绘图工具 设置环行视角 倾斜视角 前后移动视角 水平面移动视角 以 Y 方向为标准设置环行视角 选择是否打开光照 停止光照和视角的移动 边界颜色 设置字体 倾斜字体 居中 出入箭头 插入文本箭头 插入方框 为图形上的点填加 pin 编辑模式 –184– 第 5 章 数据可视化 plot([0:0.1:12],[0:0.1:12]) • 选中图形窗口中 View 菜单的前三项,并为图形填加一双箭头,如图 5-91 所示。 图 5-91 双箭头示例 • 选中双箭头,再单击 按钮在双箭头的两个箭头处分别添加 pin,然后单击 按钮移 动图形,可以发现两条曲线的相对位置不变,如图 5-92 所示。 图 5-92 为图形添加 pin –185– 第 6 章 数据分析 数据分析和处理是各种应用中非常重要的问题。针对数据分析和处理,MATLAB 7.0 提 供大量的函数方便用户使用。本章将介绍 MATLAB 7.0 强大的数据分析和处理功能。 本章 6.1 小节介绍多项式函数,这些函数用于多项值求值、多项式乘法、多项式除法、 多项式求导等。6.2 小节介绍插值函数。MATLAB 7.0 提供数个不同的插值算法,这些算法的 性能比较将在本小节中详细介绍。6.3 小节涵盖内容比较多,包括基本的数据分析函数(例如 矩阵的平均值)、协方差和相关矩阵、有限差分和梯度、信号滤波和卷积以及傅立叶变换。6.4 小节介绍功能函数,包括函数的表示方法、函数的画图、函数的最小值和最大值、函数的数 值积分以及在功能函数中使用参数的方法。6.5 小节介绍了三类微分方程组问题的解法,包括 常微分方程组的初值问题、延迟微分方程组数值解和常微分方程组的边界问题。 6.1 多项式函数 MATLAB 7.0 提供了一些处理多项式的基本函数,如求多项式的值、多项式的根和多项 式的微分等。另外,MATLAB 7.0 也提供了一些高级函数处理多项式,如曲线拟合和多项式 的部分分式表示。这些函数保存在 MATLAB 7.0 工具箱的 polyfun 子目录下,如表 6-1 所示。 表 6-1 多项式函数 函数名 conv deconv poly polyfit polyder polyint polyvar polyvarm residue roots 多项式乘法 多项式除法 求多项式的系数 多项式曲线拟合 求多项式的一阶导数 求多项式的积分 求多项式的值 求矩阵多项式的值 部分分式展开 求多项式的根 功能描述 此外在 MATLAB 7.0 的符号工具箱(Symbolic Math Toolbox)中还有一些是用于处理多 项式的函数,在表 6-1 中没有提到。 6.1.1 多项式表示法 MATLAB 7.0 采用行向量来表示多项式,将多项式的系数按降幂次序存放在行向量中。 多项式 P(x)=a0xn+ a1xn-1+...+ an-1x+an 的系数行向量为 P=[a0 a1 ...an],注意顺序必须是 从高次幂到低次幂。例如,多项式 x4+3x3+4x+5 可以用系数向量[1 3 0 4 5]来表示,注意多项 第 6 章 数据分析 式中缺少的幂次要用“0”补齐。 例如,设计一个函数 poly2str(),实现把一个行向量表示的多项式转换为常见的字符串表 示的多项式,代码设置如下: %poly2str.m %把多项式的行向量表示转换为字符串表示 function Y=poly2str(X) %X 是表示一个多项式的向量 %Y 多项式的字符串表示 %输入检查,如果 X 不是一个向量则退出 if isvector(X)==0, disp(’输入错误:输入 X 不是一个向量,请输入一个代表多项式的向量!’); return; %函数返回 end; Y=’’; %输出字符串 n=length(X); for i=1:n, %把多项式的每一次幂转换为字符串 if(i~=1&&X(i)>0) %如果是正系数,必须添加‘+’字符 Y=[Y ’+’]; end; %输出系数 if(X(i)==0), %如果该次幂系数为 0,则不输出字符串 continue; elseif(X(i)==1&&i~=n), %如果该次幂系数为 1,可以不输出系数,只输出 x^n Y=Y; else Y=[Y num2str(X(i))]; %其他情况输出系数 end; %输出 x^n if(i==n-1), %1 次幂输出字符串’x’ Y=[Y ’x’]; elseif(i==n), %0 次幂不输出 x^n Y=Y; else Y=[Y ’x^’ num2str(n-i)]; %其他情况输出 x^n end; %如果不是最后一项,输出’+’ end; if(Y(1)==’+’) %修正如果 0 次幂为 0 时,造成字符串末尾有多余的字符串’ + ’ Y(1)=[]; end; –187– MATLAB 7.0 以下语句说明了函数 poly2str()的用法: poly2str([0 1 -2 3 -5 0 1 0 -1]) 上述语句得到输出代码如下: ans = x^7-2x^6+3x^5-5x^4+x^2-1 上面字符串中 x^n 表示 x 的 n 次幂。 例如,设计一个函数实现从多项式的字符串表示转换为多项式的行向量表示,要求输入 不必降幂排列,并且函数具有实现多项式同类项合并功能,代码设置如下: %str2poly.m %把多项式的字符串表示转换为行向量表示 function Y=str2poly(X) %X 是字符串形式的多项式 %Y 是行向量形式的多项式 %输入格式检查 if(ischar(X)==0) disp(’输入错误:输入 X 必须是一个字符串!’); end; %用正则表达式寻找’+’或者’-’的下标位置 index=regexp(X,’\+|\-’); %多项式的项数 L=length(index); %用于存储多项式每一项信息的单元字符串矩阵 term=cell(1,L+1); term(1)=cellstr(X(1:(index(1)-1))); for i=1:L-1, term(i+1)=cellstr(X(index(i):(index(i+1)-1))); end; term(L+1)=cellstr(X(index(L):end)); %如果第一项为空,则删除该项 if(isempty(char(term(1)))) term(1)=[]; %多项式的项数减一 L=L-1; end; %多项式系数矩阵 coefficient=[]; %多项式幂次矩阵,它与多项式系数矩阵一一对应 power=[]; for i=1:L+1, %单项多项式字符串表达式 –188– 第 6 章 数据分析 substring=char(term(i)); %用正则表达式,寻找字符串’x^’,由于’^’是正则表达式中的特殊字符,多以用’\^’来表示 index2=regexp(substring,’x\^’); if(isempty(index2)==0), %如果匹配上 if(index2(1)==1), %单项多项式字符串为’x^*’形式 coefficient=[coefficient 1]; power=[power str2num(substring((index2(1)+2):end))]; elseif(index2(1)==2), %单项多项式字符串为’+x^*’或者’-x^*’,’3x^*’形式 if(substring(1)==’+’), coefficient=[coefficient 1]; power=[power str2num(substring((index2(1)+2):end))]; elseif(substring(1)==’-’), coefficient=[coefficient -1]; power=[power str2num(substring((index2(1)+2):end))]; end; else %单项多项式字符串为’2.2x^*’ coefficient=[coefficient str2num(substring(1:(index2(1)-1)))]; power=[power str2num(substring((index2+2):end))]; end; else %单项多项式字符串不含’x^’ %用正则表达式,寻找字符串’x’ index2=regexp(substring,’x’); if(isempty(index2)==0), %如果匹配上’x’ if(index2(1)==1), %单项多项式字符串为’x*’形式 coefficient=[coefficient 1]; power=[power 1]; elseif(index2(1)==2), %单项多项式字符串为’+x*’或者’-x*’,’3x*’形式 if((substring(1)==’+’)==1), coefficient=[coefficient 1]; power=[power 1]; elseif(substring(1)==’-’), coefficient=[coefficient -1]; –189– MATLAB 7.0 power=[power 1]; else %单项多项式字符串为’2.2x*’ coefficient=[coefficient str2num(substring(1:(index2-1)))]; power=[power 1]; end; else coefficient=[coefficient str2num(substring(1:(index2-1)))]; power=[power 1]; end; else %单项多项式字符串不含’x^’,’x’,则断定它是常数项 coefficient=[coefficient str2num(substring)]; power=[power 0]; end; end; end; %合并同类项 N=max(power)+1; Y=zeros(1,N); for i=1:N, index3=find(power==(N-i)); Y(i)=sum(coefficient(index3)); end; 以下语句把一个多项式的字符串表示转换为一个多项式的向量表示,然后再把多项式的 向量表示转回成多项式的字符串表示,代码设置如下: X=’-10x^2+8x^2-x^3+4x^5+x^3-10+9-x^7’; P=str2poly(X) Str=poly2str(P) 由上述语句得到输出代码如下: P= -1 0 4 0 0 -2 0 -1 Str = -1x^7+4x^5-2x^2-1 由上述结果可以看出,输入多项式的同类项被合并了,这正是我们设计函数 str2poly() 时所希望的。 6.1.2 多项式求值 在 MATLAB 7.0 中使用函数 polyval()来计算多项式的值。其调用方式如下: –190– 第 6 章 数据分析 • y = polyval(p,x),p 为行向量形式的多项式,x 代入多项式的值,它可以是标量、向量 和矩阵。如果 x 是向量或者矩阵,那么该函数将对向量或者矩阵的每一个元素计算多 项式的值,并返回给 y。 MATLAB 7.0 不但可以计算矩阵元素的多项式值,也可以把整个矩阵代入多项式作为自 变量进行计算。计算矩阵多项式值的函数是 polyvalm(),其调用方式如下: • Y = polyvalm(p,X),把矩阵 X 代入多项式 p 中进行计算,其中矩阵 X 必须是方阵。 例如,求多项式的值和矩阵多项式的值,请注意这两者的区别,具体代码如下: %polyval_example.m %多项式求值 str1=’x^2+2x+3’; p1=str2poly(str1); A=[1 0; 0 -1]; p1_A=polyval(p1,A) %求多项式值 p1_Am=polyvalm(p1,A) %求矩阵多项式的值 由上述语句得到输出代码如下: p1_A = 63 32 p1_Am = 60 02 6.1.3 多项式乘法和多项式除法 多项式乘法和除法运算也就是多项式向量的卷积和解卷积运算。在 MATLAB 7.0 中,函 数 conv()和 deconv()用来实现这些运算。这两个函数的调用格式如下。 • w = conv(u,v),实现向量 u,v 的卷积,在代数上相当于多项式 u 乘上多项式; • [q,r] = deconv(v,u),实现解卷积运算,他们之间的关系为 v = conv(u,q)+r。在代数上相 当于实现多项式 u 除以 v,得到商 q 和余多项式 r。 例如,求多项式 x2+2x+3 与多项式 2x2+3x+4 的乘积,代码设置如下: %poly_conv.m %多项式乘法 str1=’x^2+2x+3’; str2=’2x^2+3x+4’; p1=str2poly(str1); p2=str2poly(str2); p3=conv(p1,p2); str3=poly2str(p3) 上述语句得到输出如下: –191– MATLAB 7.0 str3 = 2x^4+7x^3+16x^2+17x+12 为了直观,上面程序利用了 6.1.1 小节中的多项式的字符串表示与多项式的向量表示的 互换函数。 例如,求上个例子中的多项式乘积结果 2x4+7x3+16x2+17x+12 除以多项式 x2+2x+3 的商, 代码设置如下: %poly_deconv.m %多项式除法 str3=’2x^4+7x^3+16x^2+17x+12’; str1=’x^2+2x+3’; p3=str2poly(str3); p1=str2poly(str1); [p2 r]=deconv(p3,p1); str2=poly2str(p2) r 由上述语句得到输出代码如下: str2 = 2x^2+3x+4 r= 00000 6.1.4 多项式的导数和微分 1.多项式的导数 在 MATLAB 7.0 中使用函数 polyder()来计算多项式的导数,其调用方式如下: • k = polyder(p),返回多项式 p 的导数; • k = polyder(a,b),返回多项式 a 与多项式 b 乘积的导数; • [q,d] = polyder(b,a),返回多项式 a 除以 b 的商的导数,并以 q/d 格式表示。 例如,对多项式求导,代码设置如下: %polyder_example.m %多项式求导数的示例 str1=’x^2+2x+3’; str2=’2x^2+3x+4’; p1=str2poly(str1); p2=str2poly(str2); %求多项式’x^2+2x+3’的导数 q1=polyder(p1); str1_der=poly2str(q1) %求多项式’2x^2+3x+4’的导数 –192– q2=polyder(p2); str2_der=poly2str(q2) %求多项式’x^2+2x+3’与’2x^2+3x+4’乘积的导数 c=polyder(p1,p2); str3_der=poly2str(c) %求多项式’x^2+2x+3’除以’2x^2+3x+4’的商的导数 [q d]=polyder(p1,p2); str_der_q=poly2str(q) str_der_q=poly2str(d) 由上述语句得到输出代码如下: str1_der = 2x+2 str2_der = 4x+3 str3_der = 8x^3+21x^2+32x+17 str_der_q = -1x^2-4x-1 str_der_q = 4x^4+12x^3+25x^2+24x+16 第 6 章 数据分析 2.多项式的积分 在 MATLAB 7.0 中使用函数 polyint()来计算多项式的积分,其调用方式如下: • polyint(p,k),返回多项式 p 的积分,设积分的常数项为 k; • polyint(p),返回多项式 p 的积分,设积分的常数项为 0。 例如,求多项式 3x2+4x+5 的积分,代码设置如下: %polyint_example.m %多项式求积分的示例 str1=’3x^2+4x+5’; p1=str2poly(str1); %求积分并设常数项为 5 p2=polyint(p1,5); str2=poly2str(p2) %求积分并设常数项为 0 p3=polyint(p1); str3=poly2str(p3) 由上述语句得到输出代码如下: str2 = x^3+2x^2+5x+5 str3 = –193– MATLAB 7.0 x^3+2x^2+5x 6.1.5 多项式的根和由根创建多项式 1.多项式的根 在 MATLAB 7.0 中使用函数 roots()来求多项式的根,其调用方式如下: • r = roots(c),返回多项式 c 的所有根 r,r 是向量,其长度等于根的个数。 例如,求多项式 2x3-2x2-8x+8 的根,代码设置如下: %roots_example.m %多项式求根 str1=’2x^3-2x^2-8x+8’; p1=str2poly(str1); r=roots(p1) %求多项式根 由上述语句得到输出代码如下: r= -2.0000 2.0000 1.0000 2.由根创建多项式 与多项式求根相反的过程是由根创建多项式,它由函数 poly()来实现,其调用格式如下: • p = poly(r),输入 r 是多项式所有根,返回值为代表多项式的行向量形式; • p = poly(A),输入是 N×N 的方阵,返回值 p 是长度为 N+1 的行向量多项式,它是矩阵 A 的特征多项式,也就是说多项式 p 的根是矩阵 A 的特征值。 例如,由根[-2 2 1]创建多项式,代码设置如下: %poly_example.m %由根创建多项式 r=[-2 2 1]; p=poly(r); str=poly2str(p) 由上述语句得到输出代码如下: str = x^3-1x^2-4x+4 例如,求矩阵的特征多项式,代码设置如下: %poly_example2.m %求矩阵 A 的特征多项式 A =[1 2 3; 4 5 6; 7 8 0]; –194– p=poly(A); str=poly2str(p) r=roots(p) %求多项式 p 的根 A_eig=eig(A) %求矩阵 A 的特征值 由上述语句得到输出代码如下: str = x^3-6x^2-72x-27 r= 12.1229 -5.7345 -0.3884 A_eig = 12.1229 -0.3884 -5.7345 第 6 章 数据分析 6.1.6 多项式部分分式展开 函数 residue()可以将多项式之比用部分分式展开,也可以将一个部分分式表示为多项式 之比。函数 residue()的调用格式如下: • [r,p,k] = residue(b,a),求多项式之比 b/a 的部分分式展开,返回值 r 是部分分式的留数, p 是部分分式的极点,k 是直接项。如果多项式 a 没有重根,部分分式的形式如下: b(x) a(x) = r1 x - p1 + r2 x - p2 + ... + rn x - pn + ks 其中向量 r,p 的长度和向量 a,b 的长度有如下关系: length(a)-1 = length(r) = length(p) 当向量 b 的长度小于 a 时,向量 k 中没有元素,否则应满足如下关系: length(k) = length(b)-length(a)+1 • [b,a] = residue(r,p,k),从部分分式得到多项式向量。 例如,求多项式之比的部分分式展开,然后在把部分分式表示为多项式之比的形式,代 码设置如下: %residue_example.m %求多项式之比的部分分式,然后把部分分式表示为多项式之比的形式 str1=’2x^3+3x^2-4x+1’; str2=’x^2-3x+2’; p1=str2poly(str1); p2=str2poly(str2); [r,p,k]=residue(p1,p2) %求多项式之比的部分分式 –195– MATLAB 7.0 [p1_res,p2_res]=residue(r,p,k); str1_res=poly2str(p1_res) str2_res=poly2str(p2_res) 由上述语句得到输出代码如下: r= 21 -2 p= 2 1 k= 29 str1_res = 2x^3+3x^2-4x+1 str2_res = x^2-3x+2 %把部分分式表示为多项式之比的形式 %把多项式显示为字符串形式 6.1.7 多项式曲线拟合 函数 polyfit()采用最小二乘法对给定数据进行多项式拟合,最后给出多项式的系数。该 函数的调用方式如下: • p = polyfit(x,y,n),采用 n 次多项式 p 来拟合数据 x 和 y,从而使得 p(x)与 y 最小均方差 最小。 例如,下面例子说明函数 polyfit()的用法,并讨论采用不同多项式阶数对拟合结果的影 响,代码设置如下: %polyfit_example.m %说明函数 polyfit()的用法,并讨论采用不同多项式阶数对拟合结果的影响 x=0:0.2:10; y=0.25*x+20*sin(x); %5 阶多项式拟合 p5=polyfit(x,y,5); y5=polyval(p5,x); %8 阶多项式拟合 p8=polyfit(x,y,8); y8=polyval(p8,x); %60 阶多项式拟合 p60=polyfit(x,y,60); y60=polyval(p60,x); %画图 hold on; –196– 第 6 章 数据分析 plot(x,y,’ro’); plot(x,y5,’b--’); plot(x,y8,’b:’); plot(x,y60,’r-.’); xlabel(’x’); ylabel(’y’); legend(’原始数据’,’5 阶多项式拟合’,’8 阶多项式拟合’,’60 阶多项式拟合’); 得到输出如图 6-1 所示。由图 6-1 可以看出:使用 5 次多项式拟合时,拟合得到的结果 比较差。而使用 8 次多项式拟合时,得到的结果与原始数据符合得很好。但使用 60 次多项式 拟合时,拟合的结果非常差。可见用多项式拟合必须选择适中的阶数,而不是阶数越高精度 越高。 图 6-1 多项式曲线拟合 6.1.8 曲线拟合图形用户接口 为了方便用户的使用,MATLAB 7.0 提供了支持曲线拟合的图形用户接口。它位于 “Figure”窗口的“Tools\Basic Fitting”菜单中。为了使用该工具,首先用待拟合的数据画图, 代码设置如下: x=0:0.2:10; y=0.25*x+20*sin(x); plot(x,y,’ro’); 得到“Figure”窗口,如图 6-2 所示。然后在“Figure”窗口中点击菜单“Tools\Basic Fitting”, 得到“Basic Fitting”窗口,如图 6-3 所示。可以单击“Basic Fitting”窗口右下角的向右按钮, 得到“Basic Fitting”窗口的全貌,如图 6-4 所示。 –197– MATLAB 7.0 图 6-2 “Figure”窗口 图 6-3 Basic Fitting 窗口 图 6-4 “Basic Fitting”窗口全貌 在 “Basic Fitting”窗口的“Plot fits”复选框中选择“cubic”和“6th degree polynomial” 这两选项,然后单击“Plot residuals”单选框使之处在选上状况,最后把“Plot residuals”单 选框正下方的下拉列表选择为“Scatter plot”。在这些操作后,“Figure”窗口已经出现拟合好 的数据,并且画出拟合数据的残留误差 residuals(如图 6-5 所示)。同时拟合结果也在“Basic Fitting”的“Numerical results”中显示出来。 –198– 第 6 章 数据分析 图 6-5 数据拟合结果 6.2 插值 插值是在已知数据之间寻找估计值的过程。在信号处理和图像处理中,插值是极其常用 的方法。MATLAB 7.0 提供大量的插值函数。这些函数在获得数据的平滑度、时间复杂度和 空间复杂度方面上有不同的性能。 插值函数保存在 MATLAB 7.0 工具箱的 polyfun 子目录下,如表 6-2 所示。 表 6-2 函数名 pchip interp1 interp1q interpft interp2 interp3 interpn griddata griddata3 griddatan spline ppval 插值函数 分段三次厄米多项式插值 一维插值 一维快速插值 一维快速傅立叶插值方法 二维插值 三维插值 N 维插值 栅格数据插值 3 维栅格数据插值 N 维栅格数据插值 三次样条插值 分段多项式求值 功能描述 6.2.1 一维插值 一位插值就是对一维函数 y=f(x)进行插值,图 6-6 显示了插值的含义,实心点(x,y)代表的 是已知数据,空心点(xi,yi)的横坐标 xi 代表需要估计值的位置,纵坐标 yi 表示插值后的估计 –199– MATLAB 7.0 值。在 MATLAB 7.0 中,一维插值有基于多项式的插值和基于快速傅立叶的插值两种类型。 图 6-6 一维插值示意图 1.一维多项式插值 一维多项式插值可以通过函数 interp1()来实现。函数 interp1()的基本调用格式如下。 • yi=interp1(x,y,xi,method),x 必须是向量,y 可以是向量也可以是矩阵。如果 y 是向量, 则必须与 x 具有相同的长度,这时 xi 可以是标量、向量和任意维矩阵,yi 与 xi 具有相 同的大小;如果 y 是矩阵,则其大小必须是[n,d1,d2,...,dk](n 为向量 x 的长度),函数 对 d1*d2*...*dk 组 y 值都进行插值; • yi = interp1(y,xi),默认 x 为 1:n,其中 n 为向量 y 的长度; • yi = interp1(x,y,xi,method),输入变量 method 用于指定插值的方法。可采用的插值方法 将在本小节详细介绍; • yi = interp1(x,y,xi,method,’extrap’),对超出数据范围的插值数据指定外推方法’extrap’; • yi = interp1(x,y,xi,method,extrapval),对超出数据范围的插值数据返回 extrapval 值,一 般设为 NaN 或者 0; • pp = interp1(x,y,method,’pp’),返回值 pp 为数据 y 的分段多项式形式。method 指定产生 分段多项式 pp 的方法,它可以是插值方法中除了’v5cubic’的任何方法。 一维插值可以指定的方法如下: (1)最邻近插值(Nearest neighbor interpolation,method=’nearest’),这种插值方法在已知 数据的最邻近点设置插值点,对插值点的数进行四舍五入。对超出范围的点将返回一个 NaN (Not a Number)。 (2)线性插值(Linear interpolation,method=’linear’),该方法是未指定插值方法时 MATLAB7.0 默认采用的方法。该方法采用直线连接相邻的两点。对超出范围的点将返回一 个 NaN(Not a Number)。 (3)三次样条插值(Cubic spline interpolation,method=’spline’),这样方法采用三次样条 函数来获得插值点。在已知点为端点情况下,插值函数至少具有相同的一阶和二阶导数。 (4)分段三次厄米多项式插值(Piecewise cubic Hermite interpolation,method=’pchip’)。 (5)三次多项式插值(method=’cubic’),与分段三次厄米多项式插值相同。 (6)MATLAB5 中使用的三次多项式插值(method=’v5cubic’),该方法使用一个 3 次多项 式函数对已知数据进行拟合。 当选择一个插值方法时应该考虑方法的执行速度、占用内存大小和获得数据的平滑度。 以上方法的特点如下。 –200– 第 6 章 数据分析 (1)最邻近插值:最快的插值方法,但是数据平滑方面最差,其得到的数据是不连续的。 (2)线性插值:比邻近插值占用更多的内存,执行速度也稍慢,但其数据平滑方面优于 邻近插值。与邻近插值不同的是,线性插值的数据变化是连续的。 (3)三次样条插值:处理速度最慢,占用内存小于分段三次厄米多项式插值,可以产生 最光滑的结果。但是如果输入数据不均匀或者某些点靠得很近,会出现一些错误。样条插值 是极其有用的插值方法,因此除了提供三次样条插值函数外,MATLAB 7.0 还提供了一个样 条插值工具箱,它位于 toolbox\splines 下。 (4)分段三次厄米多项式插值:处理速度和消耗的内存比线性插值差,但插值得到的数 据和一阶导数都是连续的。 这些方法的相对优劣不仅适用于一维插值,而且适用于二维插值情况甚至高维插值 情况。 例如,下面例子用不同插值方法对一维数据进行插值,并比较其不同,代码设置如下: %interp1_example.m %用不同插值方法对一维数据进行插值,并比较其不同 x = 0:1.2:10; y = sin(x); xi = 0:0.1:10; yi_nearest = interp1(x,y,xi,’nearset’); %最邻近插值 yi_linear = interp1(x,y,xi); %默认插值方法是线性插值 yi_spline = interp1(x,y,xi,’spline ’); %三次样条插值 yi_cubic = interp1(x,y,xi,’cubic’); %三次多项式插值 yi_v5cubic = interp1(x,y,xi,’v5cubic’); %MATLAB 5 中使用的三次多项式插值 hold on; subplot(2,3,1); plot(x,y,’ro’,xi,yi_nearest,’b-’); title(’最邻近插值’); subplot(2,3,2); plot(x,y,’ro’,xi,yi_linear,’b-’); title(’线性插值’); subplot(2,3,3); plot(x,y,’ro’,xi,yi_spline,’b-’); title(’三次样条插值’); subplot(2,3,4); plot(x,y,’ro’,xi,yi_cubic,’b-’); title(’三次多项式插值’); subplot(2,3,5); plot(x,y,’ro’,xi,yi_v5cubic,’b-’); title(’三次多项式插值(MATLAB5)’); 由上述语句得到如图 6-7 所示的示意图。 –201– MATLAB 7.0 图 6-7 一维插值 2.一维快速傅立叶插值 一维快速傅立叶插值通过函数 interpft()来实现,该函数用傅立叶变换把输入数据变换到 频域,然后用更多点的傅立叶逆变换,变换回时域,其结果是对数据进行增采样。函数 interpft() 的调用格式如下。 • y = interpft(x,n),对 x 进行傅立叶变换,然后采用 n 点傅立叶逆变换变回到时域。如果 x 是一个向量,数据 x 的长度为 m,采样间隔为 dx,则数据 y 的采样间隔是 dx*m/n, 注意 n 值必须大于 m;如果 x 是矩阵,函数操作在 x 的列上,返回结果与 x 具有相同 的列数但其行数为 n; • y = interpft(x,n,dim),在 dim 指定的维度上进行操作。 例如,利用一维快速傅立叶插值实现数据增采样,代码设置如下: %interpft_example.m %一维快速傅立叶插值实现数据增采样 x = 0:1.2:10; y = sin(x); n = 2*length(x); %增采样 1 倍 yi = interpft(y,n); %一维快速傅立叶插值 xi = 0:0.6:10.4; hold on; plot(x,y,’ro’); %画图 plot(xi,yi,’b.-’); title(’一维快速傅立叶插值’); legend(’原始数据’,’插值结果’); 由上述语句得到如图 6-8 所示的示意图。 –202– 第 6 章 数据分析 图 6-8 一维快速傅立叶插值 6.2.2 二维插值 二维插值主要应用于图像处理和数据的可视化,其基本思想与一维插值相同,它是对两 变量的函数 z=f(x,y)进行插值。二维插值的示意图如图 6-9 所示。 图 6-9 二维插值示意图 MATLAB 7.0 中的二维插值函数为 interp2(),其调用格式如下。 • zi = interp2(x,y,z,xi,yi),原始数据 x,y,z 决定插值函数 z=f(x,y),返回值 zi 是(xi,yi)在函数 f(x,y)上的值; • zi = interp2(z,xi,yi),若 z=n×m,则 x=1:n,y=1:m; • zi = interp2(z,ntimes),在两点之间递归地插值 ntimes 次; • zi = interp2(x,y,z,xi,yi,method),可采用的插值方法在本小节将详细介绍; • zi = interp2(...,method, extrapval),当数据超过原始数据范围时,用输入 extrapval 指定 一种外推方法。 二维插值可以采用的插值方法如下: (1)最邻近插值(Nearest neighbor interpolation,method=’nearest’),这种插值方法在已知 数据的最邻近点设置插值点,对插值点的数进行四舍五入。对超出范围的点将返回一个 NaN (Not a Number)。 (2)双线性插值(Bilinear interpolation,method=’linear’),该方法是未指定插值方法时 MATLAB7.0 默认采用的方法。插值点的值只决定于最邻近的 4 个点的值。 (3)三次样条插值(Cubic spline interpolation,method=’spline’),这样方法采用三次样条 –203– MATLAB 7.0 函数来获得插值数据。 (4)双三次多项式插值(method=’cubic’)。 例如,下面例子比较各种二维插值插值算法的不同,如下: %interp2_example2 %采用二次插值对三维高斯型分布函数进行插值 [x,y] = meshgrid(-3:0.8:3); %原始数据 z = peaks(X,Y); [xi,yi] = meshgrid(-3:0.25:3); %插值点 zi_nearest = interp2(x,y,z,xi,yi,’nearset’); %最邻近插值 zi_linear = interp2(x,y,z,xi,yi); %默认插值方法是线性插值 zi_spline = interp2(x,y,z,xi,yi,’spline ’); %三次样条插值 zi_cubic = interp2(x,y,z,xi,yi,’cubic’); %三次多项式插值 hold on; subplot(2,3,1); surf(x,y,z); title(’原始数据’); subplot(2,3,2); surf(xi,yi,zi_nearest); title(’最邻近插值’); subplot(2,3,3); surf(xi,yi,zi_linear); title(’线性插值’); subplot(2,3,4); surf(xi,yi,zi_spline); title(’三次样条插值’); subplot(2,3,5); surf(xi,yi,zi_cubic); title(’三次多项式插值’); figure; %新开绘图窗口 subplot(2,2,1); %画插值结果的等高线 contour(xi,yi,zi_nearest); title(’最邻近插值’); subplot(2,2,2); contour(xi,yi,zi_linear); title(’线性插值’); subplot(2,2,3); contour(xi,yi,zi_spline); title(’三次样条插值’); subplot(2,2,4); contour(xi,yi,zi_cubic); –204– 第 6 章 数据分析 title(’三次多项式插值’); 由上述代码得到的效果图如图 6-10 和图 6-11 所示。如图 6-11 所示为插值后数据的等高 线,可以用于比较插值后数据的平滑性能。 图 6-10 二维插值 图 6-11 二维插值后的等高线 –205– MATLAB 7.0 6.3 数据分析和傅立叶变换 MATLAB7.0 中与数据分析和傅立叶变换相关的函数位于目录\toolboxs\MATLAB\datafun 下,本节将分类介绍这些函数。 MATLAB 7.0 在这些函数中的约定。 • 一维统计数据可以用行向量或者列向量来表示,不管输入数据是行向量或者是列向量, 运算是对整个向量进行的; • 二维统计数据可以采用多个向量表示,也可以采用二维矩阵来表示。对二维矩阵运算 时,运算总是按列进行的。 这两条约定不仅适用于本节提到的函数,而且适用于 MATLAB 7.0 各个工具箱的函数。 6.3.1 基本数据分析函数 MATLAB 7.0 提供的基本数据分析函数的功能和调用格式如表 6-3 所示。 表 6-3 基本数据分析函数 函数名 max min mean median std var sort sortrows 功能描述 基本调用格式 C = max(A),如果 A 是向量,返回向量中的最大值;如果 A 是矩阵 返回一个包含各列最大值的行向量 求最大值 C = max(A,B),返回矩阵 A 和 B 之中较大的元素,矩阵 A、B 必须 具有相同的大小 C = max(A,[],dim),返回 dim 维上的最大值 [C,I] = max(...),多返回最大值的下标 求最小值 与最大值函数 max()调用格式一致 M = mean(A),如果 A 是向量,返回向量 A 的平均值,如果 A 是矩 求平均值 阵,返回含有各列平均值的行向量 M = mean(A,dim),返回 dim 维上的平均值 求中间值 与求平均值函数 mean()的调用格式一致 s = std(A),如果 A 是向量,返回向量的标准方差;如果 A 是矩阵, 求标准方差 返回含有各列标准方差的行向量 s = std(A,flag),用 flag 选择标准方差的定义式 s = std(A,flag,dim),返回 dim 维上的标准方差 var(X),如果 A 是向量,返回向量的方差;如果 A 是矩阵,返回含 有各列方差的行向量 方差(标准方差的平反) var(X,1),返回第二种定义的方差 var(X,w),利用 w 做为权重计算反差 var(X,w,dim),返回 dim 维上的方差 B = sort(A),如果 A 是向量,升序排列向量;如果 A 是矩阵,升序 排列各个列 数据排序 B = sort(A,dim),升序排列矩阵 A 的 dim 维 B = sort(...,mode),用 mode 选择排序方式:’ascend’为升序,’descend’ 为降序 [B,IX] = sort(...),多返回数据 B 在原来矩阵中的下标 IX B = sortrows(A),升序排序矩阵 A 的行 对矩阵的行排序 B = sortrows(A,column),以 column 列数据作为标准,升序排序矩阵 A 的行 [B,index] = sortrows(A),多返回数据 B 在原来矩阵 A 中的下标 IX –206– 第 6 章 数据分析 sum prod hist histc trapz cumsum cumprod cumtrapz 续表 B = sum(A),如果 A 是向量,返回向量 A 的各元素之和;如果 A 是 矩阵,返回含有各列元素之和的行向量 求元素之和 B = sum(A, dim),求 dim 维上的矩阵元素之和 B = sum(A, ’double’),返回数据类型指定为双精度浮点数 B = sum(A, ’native’),返回数据类型指定为与矩阵 A 的数据类型相同 B = prod(A),如果 A 是向量,返回向量 A 的各元素连乘积;如果 A 求元素的连乘积 是矩阵,返回含有各列元素连乘积的行向量 B = prod(A,dim),返回 dim 维上的矩阵元素连乘积 n = hist(Y),在 10 个等间距的区间统计矩阵 Y 属于该区间的元素个 数 画直方图 n = hist(Y,x),在 x 指定的区间统计矩阵 Y 属于该区间的元素个数 n = hist(Y,nbins),用 nbins 个等间距的区间统计矩阵 Y 属于该区间的 元素个数 hist(...),直接画出直方图 n = histc(x,edges),计算在 edges 区间内向量 x 属于该区间的元素个 直方图统计 数 n = histc(x,edges,dim),在 dim 维上统计 x 出现的次数 Z = trapz(Y),返回 Y 的梯形数值积分 梯形数值积分(等间距) Z = trapz(X,Y),计算以 X 为自变量时 Y 的梯形数值积分 Z = trapz(...,dim),在 dim 维上计算梯形数值积分 B = cumsum(A),如果 A 是向量,计算向量 A 的累计和;如果 A 是 矩阵的累加 矩阵,计算矩阵 A 在列方向上的累计和 B = cumsum(A,dim),在 dim 维上计算矩阵 A 的累计和 矩阵的累积 函数调用格式与函数 cumsum()相同 梯形积分累计 函数调用格式与函数 trapz()相同 1.最大值、最小值、平均值、中间值、元素求和 这几个函数的用法都比较简单,下面的例子用来说明这些函数的用法: %max_example %最大值、最小值、平均值、中间值、元素求和函数使用的例子 x=1:40; y=randn(1,40); %正态分布随机数据 hold on; plot(x,y); [y_max,I_max]=max(y); %求向量最大值和相应下标 plot(x(I_max),y_max,’o’); [y_min,I_min]=min(y); %求向量最小值和相应下标 plot(x(I_min),y_min,’*’); y_mean=mean(y); %求向量平均值 plot(x,y_mean*ones(1,length(x)),’:’); y_median=median(y); %求向量反差 plot(x,y_median*ones(1,length(x)),’-.’); y_sum=sum(y); %求向量元素之和 plot(x,y_sum*ones(1,length(x)),’--’); –207– MATLAB 7.0 xlabel(’x’); ylabel(’y’); legend(’数据’,’最大值’,’最小值’,’平均值’,’中间值’,’向量元素之和’); 由上述语句得到的效果如图 6-12 所示。 图 6-12 最大值、最小值、平均值、中间值、元素求和 2.标准方差和方差 向量 x 的标准方差有如下两种定义: 1 ∑ s =   1 N -1 N k =1 ( xk − x )2 2  (5-1) 1 ∑ s =   1 N N ( xk k =1 − x )2   2 (5-2) N ∑ 其中 x = xk ,N 是向量 x 的长度。 k =1 MATLAB 7.0 中默认使用(5-1)式来计算数据的标准方差。要使用(5-2)式来计算标 准方差可以使用函数调用格式 std(A,1)。 方差是标准方差的平反。对应标准方差的两种定义,方差也有两种定义。同样 MATLAB 7.0 中默认使用(5-1)式来计算数据的方差。要使用(5-2)式来计算方差可以使用函数调用 格式 var(A,1)。 例如,有一维随机变量 x,要求验证 2*x 的方差是 x 的 4 倍,2x 标准方差是 x 的 2 倍, 代码设置如下: %std_example.m %验证 2*x 的方差是 x 的 4 倍,2x 标准方差是 x 的 2 倍 –208– x=rand(1,1000); x_std=std(x); x_var=var(x); x2_std=std(2*x); x2_var=var(2*x); disp([’随机变量 x2 的标准方差与 x1 的标准方差之比 = ’ num2str(x2_std/x_std)]); disp([’随机变量 x2 的标准方差与 x1 的标准方差之比 = ’ num2str(x2_var/x_var)]); 由上述语句得到输出代码如下: 随机变量 x2 的标准方差与 x1 的标准方差之比 = 2 随机变量 x2 的标准方差与 x1 的标准方差之比 = 4 第 6 章 数据分析 3.元素排序 MATLAB 7.0 可以对实数、复数和字符串进行排序。对复数矩阵进行排序时,先按复数 的模进行排序,如果模相等则按其在区间[-π,π]上的相角进行排序。MATLAB7.0 中实现排 序的函数为 sort()。 例如,对复数矩阵排列,代码设置如下: %sort_complex.m %对复数进行排序 a = [0 -1 -0.5i 1 0.5 i -i -0.5 0.5i]; b = sort(a) 由上述语句得到输出代码如下: b= 0 -0.5000i 0.5000 0.5000i -0.5000 1.0000i 1.0000 1.0000i -1.0000 MATLAB 7.0 提供函数 sortrows()用于对矩阵的行进行排列。 例如,对字符数据进行排列,代码设置如下: %sortrows_string.m %对字符数组进行排序 a = [’hello’;’world’;’hally’;’Clayt’;’Daney’]; b = sortrows(a) %按第 1 列的字符来排序字符串 c = sortrows(a,2) %按第 2 列的字符来排序字符串 由上述语句得到输出代码如下: b= Clayt Daney hally hello world c= hally Daney –209– MATLAB 7.0 hello Clayt world 4.基本数据分析实例 例如,需要设计一个函数从已知 n 个数据中挑选出方差最小的 k 个数据。显然,如果要 穷举所有的情况,计算量将会很大。本例中采用的算法是先对数据进行排队,然后计算所有 相邻的 k 个数据的方差,选出方差最小的相邻 k 个数据就是所求的数据。本例在排序时记录 了排序后数据在未排序数据中的下标,故能同时返回方差最小的 k 个数据在原来数据中的位 置。从已知 n 个数据中挑选出方差最小的 k 个数据的函数如下: %minvar.m %x 是一个向量,k 是小于等于向量 x 长度的正标量 %返回值是 y 是 x 中反差最小的 k 个数据,i 是 y 的元素在 x 向量中的下标 function [y,i]=minvar(x,k) if(isvector(x)==0), %输入数据检查 disp(’输入数据 x 必须是向量!’); return; end; if(isscalar(k)==0) disp(’输入数据 k 必须是标量!’); return; end; if(k<=0||k>length(x)), disp(’输入数据 k 必须是是小于等于向量 x 长度的正标量!’); return; end; k=floor(k); %把 k 截断为整数 [x_sort index]=sort(x); %对数据 x 排序 n=length(x); %数据 x 的长度 for i=1:(n-k+1), %对排好序的数据求所有相邻 k 个值 std_x_sort(i)=std(x_sort(i:(i+k-1))); %的标准方差 end; [min_std_x_sort j] = min(std_x_sort); %求最小的相邻 k 个值的标准方差和下标 i=index(j:(j+k-1)); %求最小标准方差的相邻 k 个值在向量 x %中的方差 y=x(i); %求最小标准方差的 k 个值 可以用下面语句来验证以上函数的正确性,如下: %minvar_example.m %验证已知 n 个数据中挑选出方差最小的 k 个数据的函数 minvar() data = (1:10).^2; %数列 1 4 9 16 ... –210– index = randperm(10); %1:10 的随机排列 x = data(index) %随机扰乱数据 data 的排列 [y i]=minvar(x,4) %求数据 x 中方差最小的 4 个值 由上述语句得到输出代码如下: x= 16 4 36 100 9 81 1 25 64 49 y= 1 4 9 16 i= 7251 第 6 章 数据分析 6.3.2 协方差和相关系数矩阵 在数理统计中,协方差和相关函数是极其重要的随机变量的数字特征。若给定 n 维随机 变量 x=(x1,x2,…, xn),定义 ( ) Cij = E (xi − E(xi ))(x j − E(x j )) 则称 C =    c11 M cn1 … O L ccM1nnn  为随机变量 x=(x1,x2,…, xn)的协方差矩阵。定义相关系数: Rij = Cij Cii * C jj 则称 R =    R11 M Rn1 … O L R1n M Rnn    为相关系数矩阵。 在 MATLAB 7.0 中,3 维随机变量的 100 次抽样数据应该表示为 100×3 的矩阵,其中矩 阵每一列表示随机变量的一个分量,每一行代表一次抽样。 MATLAB 7.0 提供函数 cov()用于计算随机变量的协方差矩阵,其调用格式如下: • C = cov(X),计算 X 代表的随机变量的协方差矩阵。如果 X 是一个向量,返回向量的 方差;如果 X 是矩阵,返回矩阵的协方差矩阵,其对角元是该列随机变量的方差; • C = cov(x,y),x 和 y 必须是具有相同长度的向量。相当于计算 C = cov([x y]); • cov(X,1),计算协方差时采用(5-2)式的方差,默认情况采用(5-1)式定义的无偏方 –211– MATLAB 7.0 差; • C = cov(x,y,1),计算协方差时采用(5-2)式的方差,默认情况采用(5-1)式定义的无 偏方差。 MATLAB 7.0 提供函数 corrcoef ()用于计算随机变量的相关系数矩阵,其调用格式如下。 • R = corrcoef(X),返回 X 代表的随机变量的相关系数矩阵; • R = corrcoef(x,y),x 和 y 必须是具有相同长度的向量,相当于计算 C = corrcoef([x y]); • [R,P]=corrcoef(...),多返回一个 P 值用来检验不相关的几率。如果随机变量完全相关 则 P 值为 0;如果 P 值很小,例如小于 0.05,则可以认为随机变量的相关性很大; • [R,P,RLO,RUP]=corrcoef(...),多返回与相关系数矩阵 R 有相同大小的矩阵 RLO 和 RUP, RLO 和 RUP 是相关系数在置信度为 95%情况的下界和上界。 例如,计算随机变量的系数相关矩阵,代码设置如下: %corrcoef_example.m %计算矩阵的相关系数 x = randn(10000,4); % 4 个完全无关随机变量 y(:,1) = x(:,1); y(:,2) = x(:,2); y(:,3) = 0.3*x(:,3) + 0.7*x(:,4); %在第 3 个随机变量和第 4 个随机变量之间 y(:,4) = 0.2*x(:,3) + 0.8*x(:,4); %引入相关性 [r,p] = corrcoef(y) %计算相关矩阵和不相关概率矩阵 [i,j] = find(p<0.05); %寻找相关的随机变量对 [i,j] %显示相关的随机变量对的编号 由上述语句得到输出代码如下: r= 1.0000 0.0011 0.0009 -0.0017 0.0011 1.0000 0.0019 0.0028 0.0009 0.0019 1.0000 0.9870 -0.0017 0.0028 0.9870 1.0000 p= 1.0000 0.9163 0.9313 0.8642 0.9163 1.0000 0.8496 0.7778 0.9313 0.8496 1.0000 0 0.8642 0.7778 0 1.0000 ans = 43 34 可见,只有随机变量的第 3 行和随机变量的第 4 行相关系数才比较大,这正是在程序中 故意引入的相关性。 –212– 6.3.3 有限差分和梯度 第 6 章 数据分析 在 MATLAB7.0 中使用函数 diff()来计算差分,其调用格式如下。 • Y = diff(X),如果 X 是一个向量,则返回[X(2)-X(1) X(3)-X(2) ... X(n)-X(n-1)];如果 X 是 一个矩阵则返回[X(2:m,:)-X(1:m-1,:)]; • Y = diff(X,n),返回 n 阶差分,例如 diff(X,2),与语句 diff(diff(X))效果相同; • Y = diff(X,n,dim),返回在 dim 维上的 n 阶差分。 例如,利用有限差分计算正弦函数的导数,代码设置如下: %sin_diff.m %正弦函数的导数 x=0:0.1:10; %自变量 y=sin(x); %正弦函数 y_der=diff(y)./diff(x); %导数等于有限差分之比(dy/dx) hold on; x_der=x(1:(end-1)); %导数的自变量 plot(x,y,’b-’); %画图 plot(x_der,y_der,’b-.’); axis([0 10 -1.2 1.4]); %设定坐标轴范围 legend(’正弦函数’,’正弦函数的导数’); %添加图例 由上述语句得到如图 6-13 所示的示意图。 图 6-13 正弦函数及其导数 对于一个二元函数 F(x,y),可以定义它的梯度如下: ∇F= ∂F ) i + ∂F ) j ∂x ∂y 该梯度代表 F 变化最快的方向。在 MATLAB 7.0 中,用函数 gradient()来计算梯度,其调 用格式如下。 –213– MATLAB 7.0 • FX = gradient(F),F 是一个向量,返回 F 在 x 方向上的梯度,相当于计 F 的微分; • [FX,FY] = gradient(F),F 是二维矩阵,FX 是 F 在 x 方向的微分(列方向),FY 是 F 在 y 方向的微分(行方向),并假定自变量的间距是 1; • [Fx,Fy,Fz,...] = gradient(F),F 是 N 维矩阵,返回 N 个方向的微分值; • [...] = gradient(F,h),h 为一个标量,用于指定所有方向上自变量的间距; • [...] = gradient(F,h1,h2,...),用多个标量 h1,h2,…来指定各个方向上自变量的间距。 例如,计算二维高斯函数的梯度场,代码设置如下: %gradient_example.m %计算二维高斯函数的梯度场 v=-2:0.25:2; [x,y]=meshgrid(v,v); %产生自变量 x,y z= exp(-(x.^2+y.^2+0.5*x.*y)); %二维高斯函数 [px py]=gradient(z,0.25); %梯度场 contour(v,v,z,4); %画 4 条等高线 hold on; quiver(v,v,px,py); %画出梯度场 由上述语句得到如图 6-14 所示的示意图。图中椭圆是二维高斯函数的等高线,箭头的方 向代表梯度的方向,箭头的大小代表梯度的模。由图可见梯度方向总是垂直于等高线的,这 是梯度场的基本性质之一。 图 6-14 二维高斯函数的梯度场 6.3.4 信号滤波和卷积 MATLAB 7.0 中有关信号滤波和卷积的函数,如表 6-4 所示。 –214– 表 6-4 函数名 filter filter2 conv conv2 convn deconv detrend 信号滤波和卷积函数 功能描述 一维数字滤波器 二维数字滤波器(将在本书的第 9 章详细介绍) 一维卷积和多项式乘法 二维卷积(将在本书的第 9 章详细介绍) N 维卷积 反卷积和多项式除法 去除信号中的直流或者线形成分,主要用于 FFT 运算中 第 6 章 数据分析 1.一维数字滤波 MATLAB 7.0 的一维数字滤波可以用函数 filter()来实现,它是直接 II 型线性差分方程组 的解,可以用于实现 FIR 和 IIR 滤波。直接 II 型线性差分方程组的信号流程图如图 6-15 所示。 图 6-15 直接 II 型线形差分方程组 图 6-15 的线性差分方程组用如下表达式来表示: y(n) = b(1)*x(n) + b(2)*x(n-1) + ... + b(nb+1)*x(n-nb) - a(2)*y(n-1) - ... - a(na+1)*y(n-na) 其中 na 和 nb 是向量 b 和 a 的长度。该方程是 n-1 阶的线性方程。该方程表现在 Z 域的形 式为: Y (z) = b(1) + b(2)Z −1 +L + b(nb +1)Z −nb 1+ a(2)z−1 +L + a(na +1)z−na X (z) 函数 filter()的调用格式如下。 • y = filter(b,a,X),X 为用于滤波的数据,向量 a 和 b 构造一个滤波器,Y 为数据 X 通过 滤波器之后的值; • [y,zf] = filter(b,a,X),多返回一个表示数据延迟时间的量 zf。当 X 为向量时,zf 等于 max(length(a),length(b))-1; • [y,zf] = filter(b,a,X,zi),zi 为初始数据延迟,zf 等于最终数据延迟; • y = filter(b,a,X,zi,dim),在 dim 维上进行数据滤波。 如何得到滤波器的系数 b 和 a 涉及到滤波器设计问题,具体将在本书的第 9 章作出详细 介绍。在有些情况下,可以很容易得到滤波器的系数 b 和 a。例如,一个 5 阶平均值滤波器: y(n)=1/5*x(n) + 1/5*x(n-1) + 1/5*x(n-2) + 1/5*x(n-3) + 1/5*x(n-4),其系数 a 可以表示为 1,系 –215– MATLAB 7.0 数 b 可以表示为[1/5 1/5 /15 1/5 1/5]。 下面通过一个示例来实现对带噪声的正弦信号进行平均值滤波,代码设置如下: %filter_example.m %对带噪声的正弦信号进行平均值滤波 t=0:0.1:10; %时间 n = 6*randn(size(t)); %高斯白噪声 x = 40*sin(t)+n; %在正弦信号中添加噪声 a = 1; %频率值滤波器的系数 b = [1/5 1/5 1/5 1/5 1/5]; y=filter(b,a,x); %滤波 plot(t,x,’b-’); %画原始信号 hold on; plot(t,y,’r:’); %画滤波后的信号 axis([0 10 -60 65]); %设置坐标轴范围 xlabel(’时间/ts(s)’); legend(’原始数据’,’滤波后数据’); %添加图例 上述语句得到输出如图 6-16 所示。可见平均值滤波能有效去除信号的噪声。 图 6-16 平均值滤波 2.信号卷积 向量 u 和 v 的卷积为 w,则记为 w = u ⊗ v 。如果 u,v 的长度分别为 m 和 n,则 w 的长 度为 m+n-1。卷积的定义为: w(k) = ∑u( j)v(k +1− j) j 在上式中 j 可以使 u(j)和 v(k+1-j)有意义的任何值。卷积又一个重要的性质,即 –216– 第 6 章 数据分析 fft(w)=fft(u)*fft(v),在该式中函数 fft()表示信号的傅立叶变换。 MATLAB 7.0 使用函数 conv()来计算卷积。在前面第 6.1.3 小节,已经介绍了可以用该函 数来计算多项式的乘法。函数 conv()计算卷积时利用快速傅立叶变换来实现,采用的步骤如 下: • U=fft(u); • V=fft(v); • w=ifft(u,v)。 例如,计算向量的卷积,代码设置如下: %conv_example.m %计算卷积的例子 u = ones(1,15); %阶跃信号 v = zeros(1,25); v(5:25) = 0:1/20:1; %线性信号 w=conv(u,v); %卷积 subplot(3,1,1); %画图 stem(u); title(’u’); subplot(3,1,2); stem(v); title(’v’); subplot(3,1,3); stem(w); title(’w’); 由上述语句得到如图 6-17 所示的示意图。 图 6-17 信号卷积 –217– MATLAB 7.0 3.信号直流或线性成分去除 在做快速傅立叶变换之前经常需要去除信号中的直流成分或者线性成分。MATLAB 7.0 提供的 detrend()函数就是用于实现去除信号中的直流成分或者线性成分的。其调用格式如下。 • y = detrend(x),如果 x 是一个向量,从信号 x 中减去线性成分;如果 x 是一个矩阵,去 除 x 所有列中的线性成分; • y = detrend(x,’constant’),如果 x 是一个向量,从信号 x 中减去直流成分;如果 x 是一个 矩阵,去除 x 所有列中的直流成分; • y = detrend(x,’linear’,bp),从信号 x 中减去分段线性函数,分段线性函数的端点由输入 bp 决定。 例如,从信号中去除直流成分和线性成分,代码设置如下: %detrend_example.m %从信号中去除直流成分和线性成分 t = 0:0.04:5; %时间 x = 2*t + 0.5*randn(size(t)); %带线性成分的随机信号 x_no_linear=detrend(x); %去除去除线性成分 subplot(2,1,1); %画图 hold on; plot(t,x,’b-’); plot(t,x_no_linear,’b:’); axis([0 5 -2 14]); %设置坐标轴范围 title(’从信号中去除线形成分’); legend(’原始数据’,’去除线性成分的数据’); %添加图例 y = 3 + 0.5*randn(size(t)); %带直流成分的随机信号 y_no_constant = detrend(y,’constant’); %去除直流成分 subplot(2,1,2); hold on; plot(t,y,’b-’); plot(t,y_no_constant,’b:’); axis([0 5 -2 8]); %设置坐标轴范围 title(’从信号中去直流成分’); legend(’原始数据’,’去除直流成分的数据’); %添加图例 由上述语句得到如图 6-18 所示的示意图。 6.3.5 傅立叶变换 对信号进行分析通常可以在时域中进行,也可以在频域中进行,时域分析方法和频域分 析方法各有其优缺点。傅立叶变换是把信号从时域变换到频域,因此它在信号分析中占有极 其重要的地位,如滤波器设计、频谱分析等方面。 傅立叶变换既可以对连续信号进行分析,也可以对离散信号进行分析。连续信号的傅立 –218– 第 6 章 数据分析 叶变换实际上要计算傅立叶积分,这部分内容可以参见本书第 7 章。本小节只介绍离散傅立 叶变换,即 Discrete Fourier Transform(DFT)。 图 6-18 去除信号中的直流成分和线性成分 MATLAB 7.0 提供的有关傅立叶变换的函数如表 6-5 所示。 表 6-5 函数名 fft fft2 fftn ifft ifft2 ifftn fftshift nextpow2 傅立叶变换函数 功能描述 一维离散快速傅立叶变换 二维离散快速傅立叶变换 N 维离散快速傅立叶变换 一维离散快速傅立叶逆变换 二维离散快速傅立叶逆变换 N 维离散快速傅立叶逆变换 把频谱的直流成分移到中间 返回大于或等于参数值的二次幂,满足 2p>=abs(A)的最大整数 1.一维傅立叶变换和一维傅立叶逆变化 一维离散傅立叶变换的定义,如果有一个向量 x(n),其离散傅立叶变换结果为 X(k),X(k) 与 x(n)的关系如下: ∑N − j 2π (k −1) n−1 X (k) = x(n)e N , 1≤ k ≤ N , n=1 ∑ x(n) = 1 N j 2π (k -1) n-1 X (k)e N, 1≤ n≤ N , N k =1 其中 N 为向量 x(n)的长度。 在 MATLAB 7.0 中一维离散傅立叶变换由函数 fft()来实现,其调用方式如下。 • Y = fft(X),如果 X 是向量,返回向量 X 的傅立叶变换;如果 X 是矩阵,函数对矩阵 X 的每一列进行傅立叶变换; –219– MATLAB 7.0 • Y = fft(X,n),用输入 n 指定傅立叶变换的长度。如果向量 X 的长度小于 n,则做傅立叶 变换之前在向量的尾部添加 0 值,使得向量 X 为长度为 n;如果向量 X 的长度大于 n, 则把向量 X 截断为长度为 n 的向量; • Y = fft(X,[],dim),在 dim 维上进行傅立叶变换; • Y = fft(X,n,dim),在 dim 维上进行傅立叶变换,并指定傅立叶变换的长度。 在 MATLAB 7.0 中一维离散傅立叶逆变换由函数 ifft()来实现,其调用方式基本与函数 fft() 相同,只是添加一个选项,其调用方式如下。 • y = ifft(..., ’symmetric’),把频谱 X(k)看成是共轭对称。该选项在 X(k)不是严格共轭对称 时有效; • y = ifft(..., ’nonsymmetric’),该选项为默认选项。 与傅立叶变换函数经常使用的函数是 fftshift()函数。如图 6-19 所示,信号 x(n)经过 FFT 后得到频谱 X(k),X(k)的头部和尾部代表信号的低频分量大小,X(k)的中间则代表高频部分。 习惯上画频率响应曲线时把直流成分放在曲线的中间,而把高频部分放在曲线的头部和尾部。 因此频谱 X(k)不可以直接用于画频率响应曲线。为了画图时方便,MATLAB 7.0 提供函数 fftshift()用于把 FFT 变换的结果频谱 X(k)转换为适合画图显示的格式。对一维信号操作时, fftshift()函数把信号分为左半部分和右半部分,然后交换其左右部分。 图 6-19 N 为偶数时 fftshift()函数的功能示意图 fftshift()函数不但可以用于一维 FFT 结果的调整,还可以用于二维 FFT 甚至多维 FFT 的 调整,其调用格式如下。 • Y = fftshift(X),如果 X 是向量,交换向量的左部分和右部分;如果 X 是矩阵,矩阵的 第一象限和第三象限交换,第二象限和第四象限交换; • Y = fftshift(X,dim),在某一维上交换数据的左部分和右部分。 例如,计算单位冲激信号经过一个带阻滤波器前后的频谱,代码设置如下: %fliter_example.m %单位冲击信号通过带阻滤波器 t = 1:40; %信号的时间 x =zeros(size(t)); x(1) = 1; %产生单位冲击信号 –220– 第 6 章 数据分析 [b,a] = butter(10,[0.3 0.7],’stop’); %设计带阻滤波器 y = filter(b,a,x); %滤波 hold on; %画图滤波前后的时域数据 stem(t,x,’marker’,’o’); stem(t,y,’marker’,’.’); xlabel(’时间/ts(s)’); legend(’原始数据’,’滤波后数据’); fx=fft(x); %对单位冲击信号进行傅立叶变换 fx=fftshift(fx); fy=fft(y); %滤波后的信号进行傅立叶变换 fy=fftshift(fy); figure; subplot(2,1,1); %画图显示滤波前后的数据频谱 f=(t-20)/20; plot(f,abs(fx),’b-’,f,abs(fy),’b-.’); %画信号的幅频曲线 xlabel(’数字频率/\pi(rad)’); title(’幅频曲线’); legend(’原始数据的幅频曲线谱’,’滤波后数据的幅频曲线’); subplot(2,1,2); plot(f,angle(fx),’b-’,f,angle(fy),’b-.’); %画信号的相频曲线 xlabel(’数字频率/\pi(rad)’); title(’相频曲线’); legend(’原始数据的相频曲线’,’滤波后数据的相频曲线’); 由上述程序得到输出如图 6-20 所示。其中用到滤波器设计的函数 butter(),关于该函数 的详细内容将在第 9 章中介绍。 图 6-20 单位冲击信号通过带阻滤波器的时域信号 由图 6-21 可见,单位冲激信号的频谱是一个常数,它通过一个带阻滤波器后的频谱等于 带通滤波器的频率响应曲线。这个结果验证了一个信号通过一个线性系统后的频谱等于信号 频谱乘以线性系统的频率响应的结论。 –221– MATLAB 7.0 2.二维傅立叶变换 在图像处理中经常使用二维傅立叶变换来进行图像滤波等操作。在 MATLAB7.0 中二维 傅立叶变换用函数 fft2()来实现。函数 fft2()可以看成是对信号进行两次一维傅立叶变换,即 fft2(X)=fft(fft(X).’).’。函数 fft2()的调用格式如下:。 • Y = fft2(X),X 是矩阵,对矩阵 X 进行二维傅立叶变换; • Y = fft2(X,m,n),m 和 n 指定傅立叶变换的长度。如果小于该长度则在信号的尾部添加 0 值;如果大于该长度则截断信号。 图 6-21 单位冲击信号通过带阻滤波器的频域信号 例如,分析图 6-22 的频谱,代码设置如下: %fft2_example.m %分析图像的频谱 img = imread(’coins.png’); %读图像文件 f_img = fft2(double(img)); %二维 fft 变换 f_img = fftshift(f_img); %把直流成分移动频谱的中间 imshow(img); %显示图像 figure; f_img_abs = abs(f_img); %得到频谱幅度 f_img_abs = (f_img_abs-min(min(f_img_abs)))./ .. . %把频谱幅度变换到[0,255]范围内 (max(max(f_img_abs))-min(min(f_img_abs)))*255; imshow(f_img_abs); %显示频谱幅度 title(’图像的幅频分布’); –222– 第 6 章 数据分析 图 6-22 硬币 由上述语句得到图 6-22 的幅频图,如图 6-23 所示。 图 6-23 幅频图 6.4 功能函数 功能函数就是可以用其他函数作为输入变量的函数,例如一个可以画函数图像的函数 fplot()。其调用格式为 fplot(@fun,[-pi,pi])。输入变量@fun 为需要画成图的函数的函数句柄。 MATLAB 7.0 的功能函数都在目录“\toolbox\matlab\funfun”下。 6.4.1 函数的表示 在 MATLAB 7.0 中函数可以用 M 文件、匿名函数和 inline 对象来表示。其中匿名函数是 MATLAB 7.0 的新特性。例如,要表示函数 y = f (x) = 2 1+ e−x + 3 1+ e−2x 可以通过如下 M 文件来表示上面的函数: –223– MATLAB 7.0 %customized_fun.m %用户自己定义的函数 function y=customized_fun(x) y=2./(1+exp(-x))+3./(1+exp(-2*x)); 把 customized_fun.m 文件放在 MATLAB 7.0 的路径下,例如“\work”目录下,就可以使 用该函数了。例如在命令行中输入如下代码: customized_fun(magic(4)) 由上述语句得到输出代码如下: ans = 5.0000 4.7076 4.8977 5.0000 4.9865 5.0000 4.9999 4.9993 4.9998 4.9982 4.9950 5.0000 4.9630 5.0000 5.0000 4.1045 MATLAB 7.0 中也允许使用匿名函数来表示一个函数,例如可以用下面的语句表示上述 函数: %anonymous_fun.m %创建匿名函数和使用匿名函数的例子 fh = @(x)2./(1+exp(-x))+3./(1+exp(-2*x)); fh(magic(4)) 由上述语句得到输出代码如下: ans = 5.0000 4.7076 4.8977 5.0000 4.9865 5.0000 4.9999 4.9993 4.9998 4.9982 4.9950 5.0000 4.9630 5.0000 5.0000 4.1045 在 MATLAB 7.0 以前的版本中经常使用 inline()函数来把一个字符串转换为一个 inline 对 象,该 inline 对象可以用来表示函数。MATLAB 7.0 仍然可以使用 inline 对象,例如可以用如 下的代码表示上述函数: %inline_fun.m %创建 inline 函数和使用 inline 函数的例子 g = inline(’2./(1+exp(-x))+3./(1+exp(-2*x))’); g(magic(4)) 由上述语句得到的输出结果与使用匿名函数时的输出结果是一样的。有些功能函数还接 受使用字符串来表示函数,实际上相当于功能函数内部使用 inline()函数把一个字符串转换为 inline 对象。 6.4.2 函数画图 MATLAB 7.0 提供的用于函数画图的函数如表 6-6 所示。 –224– 第 6 章 数据分析 表 6-6 函数画图的函数 函数名 fplot ezplot ezplot3 ezpolar ezcontour ezmesh ezmeshc ezsurf ezsurfc 函数画图 易用的函数画图 易用的三维函数画图 易用的极坐标画图 易用的等高线画图 易用的三维网格画图 易用的混合网格和等高线画图 易用的三维彩色表面画图 易用的混合表面和等高线画图 功能描述 函数画图函数的用法比较简单,而且用法相似。下面就以常用的 fplot()函数为例来介绍 这些函数的用法。 fplot()的基本调用格式如下。 • fplot(function,limits),function 为待画图的函数,limits 是横坐标数值范围[xmin xmax], 或者是横纵坐标数值范围[xmin xmax ymin ymax]; • fplot(function,limits,LineSpec),LineSpec 指定画图的线条属性,与 plot()函数的线条属 性用法一致; • fplot(function,limits,tol),tol 指定画图相对精度,默认值时 2e-3; • fplot(function,limits,tol,LineSpec),指定画图的线条属性和画图相对精度; • fplot(function,limits,n),用于指定最少画图点数。函数画图时至少画 n+1 个点。默认值 是 1。 x+ 1 例如,画函数 y = cos( x2 + ) 在区间[0,10]上的函数图像,代码设置如下: 1 %fplot_example.m %函数画图实例 f = @(x)cos((x+1)./(x.^2+1)); %匿名函数 fplot(f,[-5 5],1e-4,’r-’); %函数画图 title(’函数 y=cos((x+1)/(x^2+1))’); xlabel(’x’); ylabel(’y’); grid; 由上述语句得到结果如图 6-24 所示的示意图。 6.4.3 函数最小值和零点 求函数最小值和函数的零点是工程上常见的问题,MATLAB 7.0 提供一些函数用于解决 这类问题。MATLAB 7.0 提供的函数如表 6-7 所示。 –225– MATLAB 7.0 表 6-7 函数名 fminbnd fminsearch fzero optimset optimget 求函数最小值和零点 功能描述 求一元函数的给定区间内的最小值 求多元函数在给定点附近的局部最小值 求一元函数的零点 设定求最小值时的优化器参数 得到求最小值时的优化器参数 图 6-24 函数画图结果 小技巧:如果需要求函数的最大值,只要先把原函数乘上一个-1 得到一个新函数,然后求这个新 函数的最小值。则新函数最小值的自变量取值与原函数最大值的的自变量取值相同。 1.一元函数最小值 一元函数在给定区间内的最小值问题可以用函数 fminbnd()来实现,其调用格式如下。 • x = fminbnd(fun, ,x1,x2),在区间[x1 x2]内寻找函数最小值。fun 为 M 文件的函数句柄或 者匿名函数,x 为最小值的自变量取值; • x = fminbnd(fun,x1,x2,options),使用 options 选项来指定的优化器的参数。options 可以 使用函数 optimset()来设定; • [x,fval] = fminbnd(...),多返回一个输出变量最小值 fval。 注意:函数 fminbnd()只能用于连续函数,并且只给出局部最小值。当最小值是在指定区间边界上 时,函数收敛的速度很慢。 例如,求正弦函数在[0,10]内的最小值,代码设置如下: %fminbnd_example1.m %求正弦函数在[0 10]内的最小值 x=fminbnd(@sin,0,10) 由上述语句得到输出代码如下: x= –226– 第 6 章 数据分析 4.7124 如果要显示计算函数最小值的过程,则可以使用 options 参数来设定,代码设置如下: %fminbnd_example2.m %求正弦函数在[0 10]内的最小值 x=fminbnd(@sin,0,10,optimset(’Display’,’iter’)) 由上述语句得到输出代码如下: Func-count x f(x) Procedure 1 3.81966 -0.627289 initial 2 6.18034 -0.102664 golden 3 2.36068 0.703928 golden 4 4.62594 -0.996266 parabolic 5 4.74595 -0.999437 parabolic 6 4.71433 -0.999998 parabolic 7 4.71238 -1 parabolic 8 4.71242 -1 parabolic 9 4.71235 -1 parabolic Optimization terminated: the current x satisfies the termination criteria using OPTIONS.TolX of 1.000000e-004 x= 4.7124 2.求多元函数的最小值 求多元函数的最小值用函数 fminsearch()来实现。使用该函数时必须指定开始矢量 X0, 此函数返回一个在矢量 X0 附近的局部最小值。其调用格式如下。 • x = fminsearch(fun,x0),在矢量 X0 附近寻找的局部最小值。fun 为 M 文件的函数句柄 或者匿名函数,x 为最小值的自变量取值; • x = fminsearch(fun,x0,options),使用 options 选项来指定的优化器的参数。options 可以 使用函数 optimset()来设定; • [x,fval] = fminsearch(...),多返回一个输出变量最小值 fval。 例如,求二维函数 f ( x) = 100( x2 − x12 )2 + (1 − x1)2 的局部最小值,代码设置如下: %fminsearch_example.m %寻找二维最小值 banana = @(x)100*(x(2)-x(1)^2)^2+(1-x(1))^2; [x,fval] = fminsearch(banana,[-1.2, 1]) 由上述语句得到输出代码如下: x= 1.0000 1.0000 fval = 8.1777e-010 –227– MATLAB 7.0 3.求一元函数的零点 可以使用函数 fzero()来求一元函数的零点。寻找一元函数零点时,可以指定一个开始点, 或者指定一个区间。当指定一个开始点时,此函数在开始点附近寻找一个使函数值变号的区 间,如果没有找到这样的区间,则函数返回 NaN。如果知道函数零点所在的区间,则可以使 用一个包含两个元素的向量来指定此区间。fzero()函数的调用格式如下。 • x = fzero(fun,x0),在 x0 点附近寻找函数的零点。返回值 x 是零点的自变量值; • x = fzero(fun,x0,options),用 options 参数指定寻找零点的优化器参数; • [x,fval] = fzero(...),多返回一个输出变量 fval,表示函数 fun 在自变量为 x 时的函数值; • [x,fval,exitflag] = fzero(...),多返回一个函数运行返回状态标志。 例如,用函数 1 fzero()来解一元非线性方程 ( x + 4)2 + 1 + ( x − 1 4)2 + 1 = 1 。该问题等价 2 于求函数 f (x) = (x + 1 4)2 +1 + (x − 1 4)2 +1 - 1 2 的零点,函数 f(x)的曲线图形如图 6-25 所示。 图 6-25 函数 f(x)的曲线图 求解方程的代码设置如下: %fzero_example.m %求解一元非线性方程 f = @(x) 1./((x+4).^2+1)+1./((x-4).^2+1)-0.5; fplot(f,[-10 10]); xlabel(’x’); ylabel(’f(x)’); x1 = fzero(f,-5) x2 = fzero(f,-3) x3 = fzero(f,3) x4 = fzero(f,5) x1_region = fzero(f,[-10,-3]) %用匿名函数来表示函数 %画出函数的图 %求某个点附近的零点 %求某个区间内的零点 –228– x2_region = fzero(f,[-3,0]) x3_region = fzero(f,[0 3]) x4_region = fzero(f,[3 10]) 由上述语句得到输出代码如下: x1 = -5.0246 x2 = -2.9587 x3 = 2.9587 x4 = 5.0246 x1_region = -5.0246 x2_region = -2.9587 x3_region = 2.9587 x4_region = 5.0246 第 6 章 数据分析 注意:fzero()函数只能返回一个局部零点,不能寻找出所有的零点。此外,fzero()函数的收敛速度 与开始点的选取或者区间的选取有关系,尽可能先猜出零点的大概范围,然后在该范围内 寻找零点。 4.优化器参数 在求一元函数、多元函数最小值以及求一元函数零点时,都可以设定求优化器的参数。 设定优化器参数的函数为 optimset(),其调用格式如下: • options = optimset(’param1’,value1,’param2’,value2,...),用参数名和对用的参数值设定优 化器的参数。返回值 options 是一个结构体; • optimset,显示优化器的所有参数名和有效的参数值; • options = optimset,返回一个优化器的结构体,该结构体的所有属性局为空矩阵; • options = optimset(optimfun),返回求最小值函数 optimfun 对应的优化器参数; • options = optimset(oldopts,’param1’,value1,...),在原优化器参数 oldopts 的基础上,改动 某些优化器参数; • options = optimset(oldopts,newopts),用 oldopts 和 newopts 组和得到最终的优化器参数。 newopts 的所有非空参数覆盖 oldopts 中的值。 函数 optimset()中常用的优化器参数如表 6-8 所示。 –229– MATLAB 7.0 表 6-8 优化器参数 参数名 Display FunValCheck MaxFunEvals MaxIter OutputFcn TolFun TolX 有效参数值 ’off’,’iter’,’final’和’notify’ ’off’和’on’ 正整数 正整数 用户定义的函数句柄或 者空矩阵 正标量 正标量 功能描述 ’off’:不显示计算结果 ’iter’:显示每个迭代步骤的计算结果 ’final’:只显示最终结果,该选项为默认值 ’notify’:只在计算不收敛时显示计算结果 ’off’:不对输入函数的返回值进行检查,该选项为默认 值 ’on’:如果输入函数的返回值为复数或者 NaN 则显示警 告信息 最大允许的函数赋值次数 最大允许的迭代次数 空矩阵:迭代过程采用 MATLAB 7.0 自带的函数 用户自定义函数句柄:用该函数替换 MATLAB 7.0 自带 的函数 函数值的截断阈值 自变量的截断阈值 如果要得到最小优化器的参数,则可以使用函数 optimget(),其调用格式如下: • val = optimget(options,’param’),返回最小优化器参数’param’的值; • val = optimget(options,’param’,default),返回最小优化器参数’param’的值,如果该值为 空则返回 default。 6.4.4 数值积分 MATLAB 7.0 提供的数值积分函数如表 6-9 所示。 表 6-9 参数名 quad quadl quadv dblquad triplequad 数值积分函数 功能描述 一元函数的数值积分,采用自适应 Simpson 方法 一元函数的数值积分,采用自适应 Lobatto 方法 一元函数的矢量数值积分 二重积分 三重积分 1.一元函数的数值积分 MATLAB 7.0 提供了两个函数 quad()和 quadl()计算一元函数的积分。函数 quad()采用低 阶的自适应递归 Simpson 方法,函数 quadl()则采用高阶的自适应 Lobatto 方法。函数 quad() 函数的调用格式如下: • q = quad(fun,a,b),计算函数 fun 在[a b]区间内的定积分。fun 为函数句柄,a 和 b 都是 标量,它们分别是积分区间的下界和上界; • q = quad(fun,a,b,tol),以绝对误差容限 tol 计算函数 fun 在[a b]区间内的定积分,取代 MATLAB 7.0 中默认的绝对误差容限值 10-6; • q = quad(fun,a,b,tol,trace),当 trace 为非零值时,显示计算定积分的迭代过程中向量[fcnt a b-a Q]的值; • [q,fcnt] = quadl(fun,a,b,...),多返回一个输出变量 fcnt,表示计算定积分过程中计算函数 –230– 第 6 章 数据分析 值的次数。 例如,求归一化高斯函数的在区间[-1 1]上的定积分,代码设置如下: %quad_exam.m %求归一化高斯函数的在区间[-1 1]上的定积分,并得到积分过程的中间结点 y=@(x)1/sqrt(pi)*exp(-x.^2); %归一化高斯函数 quad(y,-1,1,2e-6,1) %求定积分,并显示中间迭代过程 fplot(y,[-1 1],’b’); %画出函数 hold on; %跟踪数据(运行完上面程序后,可以在命令行中复制这些数据) trace = [ 9 -1.0000000000 5.43160000e-001 0.1804679399; 11 -1.0000000000 2.71580000e-001 0.0728222057; 13 -0.7284200000 2.71580000e-001 0.1076454255; 15 -0.4568400000 9.13680000e-001 0.4817487615; 17 -0.4568400000 4.56840000e-001 0.2408826755; 19 -0.4568400000 2.28420000e-001 0.1142172651; 21 -0.2284200000 2.28420000e-001 0.1266655031; 23 0.0000000000 4.56840000e-001 0.2408826755; 25 0.0000000000 2.28420000e-001 0.1266655031; 27 0.2284200000 2.28420000e-001 0.1142172651; 29 0.4568400000 5.43160000e-001 0.1804679399; 31 0.4568400000 2.71580000e-001 0.1076454255; 33 0.7284200000 2.71580000e-001 0.0728222057]; x1 = trace(:,2); %积分过程的中间结点 y1 = y(x1); %中间结点的函数值 plot(x1,y1,’ro’); %画图 xlabel(’x’); ylabel(’y’); legend(’高斯函数’,’求积分过程的中间结点’); 由上述语句得到输出代码如下: ans = 0.84270079883874 积分过程如图 6-26 所示。 函数 quadl()的调用格式与函数 quad()相同,可以参见上面 quad()函数的用法。 在一维积分的过程中,有可能得到 3 种警告信息: • ’Minimum step size reached’,已经达到了最小步长。这意味着积分的迭代区间比给定的 定积分区间的截断误差值小。一般来说,这表明被积函数的可积性是奇异的; • ’Maximum function count exceeded’,计算函数值的次数超过 10000,一般来说,这表明 被积函数的可积性是奇异的; • ’Infinite or Not-a-Number function value encountered’,积分过程中出现浮点数溢出或者 被 0 除。 –231– MATLAB 7.0 图 6-26 积分过程 2.矢量积分 ò 矢量积分相当于多个一元定积分,例如求 1 -1 1 exp(2p * n x2 2n2 )dx, n = 1, 2, 3, 4,5 就可以用矢量积分,代码设置如下: %quadv_exam.m %求矢量积分 y=@(x,n)1./(sqrt(2*pi).*(1:n)).*exp(-x.^2./(2*(1:n).^2)); %归一化高斯函数 quadv(@(x)y(x,5),-1,1) 由上述语句得到输出代码如下: ans = 0.6827 0.3829 0.2611 0.1974 0.1585 矢量积分的结果是一个向量,其每一元素的值为一个一元函数定积分的值。 3.二重和三重积分 二重积分的形式如下: ymax xmax Q= 蝌 f (x, y)dxdy ymin xmin MATLAB 7.0 中使用函数 dblquad()来计算二重积分。该函数先计算内积分值,然后利用 内积分的中间结果来计算二重积分。根据 dxdy 的顺序,称 x 为内积分变量,y 为外积分变量。 函数 dblquad()的基本格式如下: • q = dblquad(fun,xmin,xmax,ymin,ymax),计算二元函数 fun 在矩形区域[xmin,xmax, ymin,ymax]上的二重积分。fun 为函数句柄,xmin、ymin、xmax 和 ymax 都是标量,它 们分别是积分区间的下界和上界; –232– 第 6 章 数据分析 • q = dblquad(fun,xmin,xmax,ymin,ymax,tol),用 tol 指定绝对计算精度; • q = dblquad(fun,xmin,xmax,ymin,ymax,tol,method),用 method 指定计算一维积分时采用 的函数。MATLAB7.0 默认采用函数 quad()来计算一维积分,可以设 method=@quadl, 从而采用函数 quadl()来计算一维积分。用户还可以编写自己的一维积分函数以用于计 算二维积分,该函数的调用格式必须与函数 quad()一致。 例如,计算二维高斯函数在矩形区间[-1 1 -1 1]上的二重积分,代码设置如下: %dblquad_exam.m %计算二维高斯函数在矩形区间[-1 1 -1 1]上的二重积分 f=@(x,y)1/sqrt(pi)*exp(-x.^2)*1/sqrt(pi)*exp(-y.^2); %归一化高斯函数 dblquad(f,-1,1,-1,1,1e-6,@quadl) 由上述语句得到输出代码如下: ans = 0.71014343481071 函数 dblquad()处理的都是矩形积分区域。若要计算非矩形的积分区间的二重积分,可以 用一个大的矩形积分区域包含非矩形的积分区间,然后在非矩形的积分区间之外的区域上把 二元函数的值取零。 例如,计算二维高斯函数在圆形区域 x2 + y2 < 1 上的二重积分,代码设置如下: %dblquad_exam2.m %计算二维高斯函数在圆形区域 sqrt(x.^2+y.^2)<1 上的二重积分 %在圆形区域外填充 0 的归一化高斯函数 f=@(x,y)(1/sqrt(pi)*exp(-x.^2)*1/sqrt(pi)*exp(-y.^2)).*(sqrt(x.^2+y.^2)<=1); dblquad(f,-2,2,-2,2,1e-6,@quadl) 由上述语句得到输出代码如下: ans = 0.6321 三重积分的的形式如下: zmax ymax xmax Q= 蝌 f (x, y, z)dxdydz zmin ymin xmin 三重积分可以用函数 triplequad()来实现,其用法与二重积分类似。 6.4.5 在功能函数中使用含参函数 在很多情况下,例如要求不同 a 和 b 情况下,计算函数 f (x) = ex + a * x - b 的零点。 这时在功能函数中就要使用含参函数,它有两种方法:使用嵌套函数和使用匿名函数。 1.用嵌套函数提供函数参数 一个使用含参函数的方法是编写一个 M 文件,该文件首先把含参函数的参数当作输入, –233– MATLAB 7.0 然后调用功能函数来处理含参函数,最后把含参函数以嵌套函数的方式包含在 M 文件中。例 如,求函数 f (x) = ex + a * x - b 的零点,代码设置如下: %fzero_nestedfun.m %用嵌套函数的方法实现处理含参函数 function y = fzero_nestedfun(a,b,x0) %a,b 是含参函数的参数值 %x0 是求函数零点的开始点 options = optimset(’Display’, ’off’); %关闭显示 y = fzero(@para_fun,x0,options) %求函数零点 function y=para_fun(x) %含参函数 y=exp(x)+a*x-b; end end 例如求 a=1,b=[-10 10]时,含参函数的零点取值随着参数 b 变化的关系,可以通过如下程 序来实现: %fzero_nestedfun_exam.m %使用含参函数的例子 clear; a=1; %参数取值 b=-10:10; x = zeros(size(b)); for i=1:length(b), x(i) = fzero_nestedfun(a,b(i),0); %求函数零点 end; plot(b,x); %画图 xlabel(’b’); ylabel(’零点’); 由上述语句得到如图 6-27 所示的示意图。 –234– 图 6-27 零点轨迹 2.用匿名函数提供函数参数 第 6 章 数据分析 使用含参函数还可以利通过匿名函数来实现。由于与函数 fzero()一样,一般功能函数只 接受一个自变量,因此函数的参数在使用之前必须先赋值。具体步骤如下: • 首先创建一个含参函数,保存为 M 文件格式。函数输入为自变量 x 以及函数参数; • 在调用功能函数的 M 文件中给参数赋值; • 用含参函数创建匿名函数; • 把匿名函数句柄传递给功能函数计算。 例如,用匿名函数来实现上小节的例子,首先建立含参函数,其代码设置如下: %指数函数与线性函数的交点 function y=exp_linear(x,a,b) y=exp(x)+a*x-b; 然后,用如下代码来实现计算函数零点: %fzero_anonymousfun.m %用匿名函数提供函数参数 clear; a=1; %参数取值 b=-10:10; x = zeros(size(b)); for i=1:length(b), f = @(x) exp_linear(x,a,b(i)); x(i) = fzero(f,0); %求函数零点 end; plot(b,x); %画图 xlabel(’b’); ylabel(’零点’); 由上述语句得到的输出图形与图 6-27 是一致的。 6.5 微分方程组数值解 在 MATLAB 7.0 中可以计算 3 类微分方程数值解问题,即常微分方程组的初值问题、延 迟微分方组和常微分方程组的边界问题。 6.5.1 常微分方程组的初值问题 MATLAB 7.0 可以解决 3 种常微分方程组的初值问题,即显式常微分方程组、线性隐式 常微分方程组和完全隐式常微分组。显式常微分方程组的初值问题的形式如下: y ’= f (t, y) y(t0 ) = y0 其中 y’,y 和 y0 为向量,故可以代表常微分方程组。显式常微分方程组的初值问题即已 –235– MATLAB 7.0 知常微分方程的初值 y(0),求 y 随时间的变化 y(t)。 线性隐式常微分方程组可以表述为如下形式: M (t, y) y ’= f (t, y) 该形式也称为加权常微分方程组。 完全隐式常微分组可以表述为为如下形式: y(t0 ) = y0 f (t, y, y ’) = 0 y(t0 ) = y0 以上介绍的都是一阶常微分方程组问题,如果遇到高阶常微分方程则可以先把高阶常微 分方程转换为一阶常微分方程组。一个任意的高阶常微分方程可以表述如下: y(n) = f (t, y, y ’,L , y(n- 1) ) 上式可以化为如下一阶常微分方程组: y1¢= y2 y2¢= y3 M yn¢= f (t, y1, y2 ,L , yn ) 1.显式常微分方程组的解法 由于显式常微分方程组的多样性,需要根据显式常微分方程组的特点采用相应的解法来 处理。MATLAB7.0 给出 7 种解法,分别由 7 个不同的函数来实现,它们是函数 ode45()、ode23(), ode113()、ode15s()、ode23s()、ode23t()和 ode23tb()。 在介绍这些函数之前,首先介绍一个概念——刚性(stiffness)。定性地讲,对一个常微分 方程组,如果其 Jocabian 矩阵的特征值相差十分悬殊,那么这个方程组就称为刚性方程组。 对于刚性方程组,为了保持解法的稳定,步长选取很困难。有些解法不能用于解刚性方程组, 而有些解法对方程组的刚性要求不严,可以用于解刚性问题。表 6-10 对 MATLAB 7.0 中的 7 个解法进行了对比。 表 6-10 函数名 采用算法 ode45() 四阶/五阶龙格-库 塔 ode23() ode113() ode15s() 二阶/三阶龙格-库 塔 可 变 阶 Adams PECE 算法 可变阶的数值微分 公式算法(NDFS) 常微分方程组解法对比 精度 适用系统 中 非刚性方程 低 非刚性方程 低~高 非刚性方程 低~中 刚性方程 优缺点 属于单步算法(只需要前一步的解即可计算 出当前的解),不需要附加初始值,因而, 计算过程中随意改变步长也不会增加任何 计算量。通常 ode45()对很多问题来说都是 首选的最好方法 属于单步算法,在误差容许范围较宽或者存 在轻微刚度时性能比 ode45()好 属于多步解法(需要前几步的解来计算当前 解)。比 ode45()更适合解决误差允许范围比 较严格的情况 属于多步解法。如果 ode45()解法速度很慢, 很可能系统是刚性的,可以尝试采用该算法 –236– 第 6 章 数据分析 续表 函数名 ode23s() ode23t() ode23tb 采用算法 精度 基于改进的 低 Rosenbrock 公式 自由内插实现的梯 低 形规则 TR-BDF2 方法,即 龙格-库塔公式的第 低 一级采用梯形规则、 第二级采用 Gear 法 适用系统 刚性方程 轻微刚性方程 刚性方程 优缺点 属于单部解法。比 ode15s()更适用于误差容 许范围较宽的情况。可以解决一些 ode15s() 效果不好的刚性方程 适用于轻微刚性系统,给出的解无数值衰减 对于误差允许范围比较宽的情况,比 ode15s 效果好 解显式常微分方程的 7 个函数的调用格式是完全一样的,下面以函数 ode45()为例来说明 它们的基本调用格式: • [t,Y] = ode45(odefun,tspan,y0),odefun 为代表常微分方程的方程组,其格式为 y ’= f (t, y) ,其中 t 是一个标量,y 是一个列向量,y ’是与 y 具有相同长度的列向量。 tspan 可以是两个元素的向量[t0 tf],这时函数返回 t0 到 tf 时间范围内的常微分方程的 解;tspan 也可以是[t0,t1,...,tf],这时函数返回在时间[t0,t1,...,tf]上的常微分方程的解。 y0 是 y 具有相同长度的列向量,用于指定初始值; • [t,Y] = ode45(odefun,tspan,y0,options),options 参数用于设定微分方程解法器的参数。 options 可以由函数 odeset()来获得。 例如,求非刚性方程 yⅱ- m(1- y2 ) y + y = 0 ,其中 µ 是参数,在本例中 µ =1。首先是 要把该二阶常微分方程改写为一阶常微分方程组: y1¢= y2 y2¢= m(1- y12 ) y2 - y1 然后,把该一阶常微分方程组用一个 M 文件形式的函数来表述,代码设置如下: %ivpodefun.m %常微分方程 function dydt = ivpodefun(t,y) dydt = zeros(2,1); dydt(1) = y(2); dydt(2) = (1-y(1)^2)*y(2)-y(1); 注意:表示一个微分方程的函数必须有时间 t 这个输入变量,然而在函数内部计算时可以不使用 t 这个变量。 最后,用函数 ode45()来解这微分方程,并画图计算结果,代码设置如下: %ode45_example.m %解常微分方程 [t,y] = ode45(@ivpodefun,[0 20],[2; 0]); plot(t,y(:,1),’-’,t,y(:,2),’--’) –237– MATLAB 7.0 title(’常微分方程的解’); xlabel(’t’); ylabel(’y’); legend(’y’,’y 的一阶导数’); 由上述语句得到如图 6-28 所示的示意图。 如果要处理含参数的微分方程组可以把参数作为表示微分方程组的函数的第 3 个输入变 量。然后再调用函数 ode45()时,把参数值作为最后一个输入变量。其微分方程组可以表示为: %ivpodefun_para.m %常微分方程 function dydt = ivpodefun_para(t,y,u) dydt = zeros(2,1); dydt(1) = y(2); dydt(2) = u*(1-y(1)^2)*y(2)-y(1); 如果要求参数 u=10 的微分方程组,可以用如下代码实现: [t,y] = ode45(@ivpodefun_para,[0 20],[2; 0],[],10) 图 6-28 常微分方程解 2.设置常微分方程组解法器参数 正如上小节介绍的,在调用常微分方程组函数时可以输入一个 options 结构体,从而设置 解法器的参数。该 options 结构体可以用函数 odeset()来设定,其调用格式如下: • options = odeset(’name1’,value1,’name2’,value2,...),用参数名和参数值对来设定解法器的 参数; • options = odeset(oldopts,’name1’,value1,...),修改原来的解法器 options 结构体 oldopts, 只改变指定的某些参数的值; • options = odeset(oldopts,newopts),合并两个解法器 options 结构体 oldopts 和 newopts, 这两个结构体中值不同的参数,采用 newopts 中的参数值; • odeset,显示所有的参数值和它们的默认值。 –238– 常微分方程组解法器参数如表 6-11 所示。 第 6 章 数据分析 表 6-11 常微分方程组解法器参数 参数名 RelTol AbsTol NormControl OutputFcn OutputSel Refine Stats Jacobian JPattern Vectorized Events Mass MStateDependence MvPattern MassSingular InitialSlope MaxStep InitialStep MaxOrder BDF 可取值 正标量 默认值 1e-3 正标量或者 向量 1e-6 ’on’或者’off’ ’off’ 函数句柄 @odeplot 或 者[] 正整数向量 所有分量的 下标 正整数 1 或者 4 (ode45) ’on’或者’off’ ’off’ 函数或者常 无 数矩阵 稀疏矩阵 无 ’on’或者’off’ ’off’ 函数 常数矩阵或 者函数 ’none’ ’weak’ 或者’strong’ 无 无 ’weak’ 稀疏矩阵 无 ’yes’ ’no’或 者 ’maybe’ 向量 正标量 正标量 1,2,3,4, 5 ’on’或者’off’ ’maybe’ 无 自动选择 自动选择 5 ’off’ 用途描述 用于所有分量的相对误差,解法器的积分估计误差必 须小于相对误差与解的乘积并且小于绝对误差 绝对误差允许范围,如果是标量该绝对误差应用于所 有的分量;如果是向量则单独指定每一个分量的绝对 误差 如果该值为’on’,解法器采用积分估计误差的模来控 制计算精度;如果该值为’off ’,解法器采用更加严格 的精度控制策略,即严格控制每一分量的计算精度 每个时间步长计算完后,解法器调用该函数来输出。 如果调用 ode 类函数时,没有输出变量,则默认采用 odeplot 来输出数据;如果有输出变量,则默认值是[], 即不输出结果。可选的输出函数有: odeplot:一维时域画图 odephas2:二维相位平面画图 odephas3:三维相位平面画图 odeprint:在命令行输出解 OutputSel 向量包含的下标所对应的分量被送给输出 函数 OutputFcn 输出。默认情况下,所有分量都输出 如果 refine 大于 1,则输出结果被插值,从而提供输 出结果的精度 如果该值为’on’,输出计算耗费时间,否则不输出 指定常微分方程的 Jacobian 矩阵 给出微分方程的 Jacobian 矩阵稀疏样式,如果微分方 程的 Jacobian 矩阵的元素不为 0 则 JPattern 相应元素 为 1,否则为 0 ode 函数是否被向量化,如果该值等于 1,则 ode 函 数 x 形式为[f(t,y1) f(t,y2) ...],否则返回格式为 f(t,[y1 y2 ...]) 定位事件 指定线性隐式常微分方程组的加权函数 M(t,y) 说明加权函数 M(t,y)是否依赖于 y 抖(M (t, y)v) / y 的稀疏矩阵样式。用于加权矩阵 M 与 y 强相关的情况 加权函数 M(t,y)是否奇异 初始一阶导数值 yp0,满足 M(t0,y0)*yp0 = f(t0,y0) 最大步长值 解法器自动选择初始步长 ode15s 采用的最大阶数 在 ode15s 算法中是否采用 BDF 算法 例如,设置解法器的输出函数为二维相位平面画图,代码设置如下: %odeset_example.m %解常微分方程 –239– MATLAB 7.0 option = odeset(’RelTol’,1e-6,’OutputFcn’,’odephas2’); [t,y] = ode45(@ivpodefun,[0 20],[2; 0],option); xlabel(’y’); ylabel(’y 的一阶导数’); 由上述语句得到如图 6-29 所示的示意图。 图 6-29 二维相位平面画图 3.线性隐式常微分方程组 线性隐式常微分方程组可以利用 ode 类函数的解法器参数 options 来解决。首先创建 M 文件函数来表示加权函数 M(t,y),然后用 odeset()函数来把加权函数设置到解法器的 Mass 参 数中,最后用 ode 类函数来解微分方程组。 例如,求解线性隐式常微分方程组 ( y + 1) y ’= y2 + 2 y - 2 ,首先创建表示微分方程的 函数 f(t,y)和 M(t,y),表示 f(t,y)的函数,代码设置如下: %odefun_LinImp.m %线性隐式常微分方程 function dydt = odefun_LinImp(t,y) dydt = y.^2 + 2*y -2; 表示 M(t,y)的函数,代码设置如下: %odefun_LinImp_mass.m %线性隐式常微分方程 function mass = odefun_LinImp_mass(t,y) mass = y + 1; 求解微分方程,代码设置如下: %ode_LinImp_example.m %解常微分方程 option = odeset(’RelTol’,1e-6,’OutputFcn’,’odeplot’,’Mass’,@odefun_LinImp_mass); –240– [t,y] = ode45(@odefun_LinImp,[0 2],2,option); xlabel(’t’); ylabel(’y’); 由上述语句得到如图 6-30 所示的示意图。 第 6 章 数据分析 图 6-30 线性隐式常微分方程的解 4.完全隐式常微分方程组 MATLAB 7.0 的新特性之一是提供了一个完全隐式常微分方程组的解法器 ode15i()。其 调用格式如下: • [t,Y] = ode15i(odefun,tspan,y0,yp0),odefun 是代表完全隐式常微分方程组的函数,其形 式为 f(t,y,y’)。tspan 可以是两个元素的向量[t0 tf],这时函数返回 t0 到 tf 时间范围内的 常微分方程的解;tspan 也可以是[t0,t1,...,tf],这时函数返回在时间[t0,t1,...,tf]上的常微 分方程的解。y0 和 yp0 用于指定微分方程的初始值,初始值必须是自洽的,即满足 f(t,y0,yp0)=0; • [t,Y] = ode15i(odefun,tspan,y0,yp0,options),用 options 结构体设定解法器的参数,options 结构体可以由函数 odeset()来得到。 处理完全隐式常微分方程组的一个重要问题是如何得到自洽的初始值。MATLAB 7.0 提 供函数 decic()来得到自洽的初始值。其调用格式如下: • [y0mod,yp0mod] = decic(odefun,t0,y0,fixed_y0,yp0,fixed_yp0),odefun 是代表完全隐式常 微分组的函数。t0 值为初始时间,y0 和 yp0 是猜测的初始值。函数 decic()会通过不断 改变初始值,最终得到自洽的初始值。如果希望在改变初始值的过程中保持某一个分 量不变,可以通过 fixed_y0 输入来实现。当 fixed_y0(i)=1 时,y0(i)不会被改变。fixed_yp0 的作用与 fixed_y0 是一样的; • [y0mod,yp0mod] = decic(odefun,t0,y0,fixed_y0,yp0,fixed_yp0,options),用 options 结构体 设定解法器的参数,options 结构体可以由函数 odeset()来得到。 例如,解完全隐式常微分方程 Weissinger 方程,其形式如下: ty2 ( y ’)3 - y3 ( y ’)2 + t(t2 + 1) y ’- t2 y = 0 当初值等于 3/ 2 时,方程的解析解为 y(t) = t2 + 0.5 。本例将用数值解法来求解这 –241– MATLAB 7.0 个问题。首先创建一个代表该方程的 M 文件函数,代码设置如下: %ode_weissfun.m %代表 Weissinger 方程的 M 文件函数 function exp = ode_weissfun(t,y,dydt) exp = t*y.^2*dydt.^3-y.^3*dydt.^2+t*(t^2+1)*dydt-t^2*y; 解微分方程的程序,代码设置如下: %ode15i_example.m %解完全隐式微分方程 t0 = 1; %猜想的初值 y0 = sqrt(3/2); yp0 = 0; [y0,yp0] = decic(@ode_weissfun,t0,y0,1,yp0,0); %求出自洽初值,并保值 y0 值不变 [t,y] = ode15i(@ode_weissfun,[1 10],y0,yp0); %求在[1 10]区间内的解 ytrue = sqrt(t.^2 + 0.5); %解析解 plot(t,y,t,ytrue,’o’); %画图比较数值解与解析解 xlabel(’t’); ylabel(’y’); % 由上述语句得到如图 6-31 所示的示意图。 6.5.2 延迟微分方程组数值解 延迟微分组方程的形式如下: y¢(t) = f (t, y(t), y(t - t 1),L , y(t - t k )) 在 MATLAB 7.0 中使用函数 dde23()来解延迟微分方程。其调用格式如下: 图 6-31 完全隐式常微分方程解 • sol = dde23(ddefun,lags,history,tspan),其中 ddefun 是代表延迟微分方程的 M 文件函数, ddefun 的格式为 dydt = ddefun(t,y,Z),t 是当前时间值,y 是列向量,Z(:,j)代表 y(t - t k ) , –242– 第 6 章 数据分析 而 t k 值在第二个输入变量 lags(k)中存储。history 为 y 在时间 t0 之前的值,可以有 3 种方式来指定 history。第 1 种是用一个函数 y(t)来指定 y 在时间 t0 之前的值;第 2 种 方法是用一个常数向量来指定 y 在时间 t0 之前的值,这时 y 在时间 t0 之前的值被认为 是常量;第 3 种以前一时刻的方程解 sol 来指定时间 t0 之前的值。tspan 是两个元素的 向量[t0 tf],这时函数返回 t0~tf 时间范围内的延迟微分方程组的解; • sol = dde23(ddefun,lags,history,tspan,option),option 结构体用于设置解法器的参数, option 结构体可以由函数 ddeset()来获得。 函数 dde23()的返回值是一个结构体,它有 7 个属性,其中重要的属性有如下 5 个: • sol.x,dde23 选择计算的时间点; • sol.y,在时间点 x 上的解 y(x); • sol.yp,在时间点 x 上的解的一阶导数 y’(x); • sol.history,方程初始值; • sol.solver,解法器的名字’dde23’。 其他两个属性为 sol.stat 和 sol. discont。 如果需要得到在[t0 tf]之间 tint 时刻的解,可以使用函数 deval,其用法为:yint = deval(sol,tint),yint 是在 tint 时刻的解。 例如,求解如下延迟微分方程组: 初始值为: y ’1 = y12 (t - 3) + y22 (t - 1) y ’2 = y1(t) + y2 (t - 1) y1(t) = 1 y2 (t) = t - 2 (t < 0) 首先,确定延迟向量 lags,在本例中 lags=[1 3]。 其次,创建一个 M 文件形式的函数表示延迟微分方程组,代码设置如下: %ddefun.m %延迟微分方程 function dydt=ddefun(t,y,Z) dydt = zeros(2,1); dydt(1) = Z(1,2).^2 + Z(2,1).^2; dydt(2) = y(1) + Z(2,1); 然后,创建一个 M 文件形式的函数表示延迟微分方程组的初始值,代码设置如下: %ddefun_history.m %延迟微分方程的历史函数 function y=ddefun_history(t) y = zeros(2,1); y(1) = 1; y(2) = t-2; –243– MATLAB 7.0 最后,用 dde23 解延迟微分方程组和用图形显示解,代码设置如下: %dde_example.m %解延迟微分方程的例子 lags = [1 3]; %延迟向量 sol = dde23(@ddefun,lags,@ddefun_history,[0,1]); %解方程 hold on; plot(sol.x,sol.y(1,:),’b-’); %画出结果 plot(sol.x,sol.y(2,:),’r-.’); title(’延迟微分方程的解’); xlabel(’t’); ylabel(’y’); legend(’y_1’,’y_2’,2); 由上述语句得到如图 6-32 所示的示意图。 图 6-32 延迟微分方程组的解 6.5.3 常微分方程组的边界问题 常微分方程组的边界问题的形式如下: dy = f (x, y) dx 同时要指定函数 y(x)在某些边界条件下的值,然后在边界条件下求函数 y(x),其中 x 是 独立变量。MATLAB 7.0 中使用函数 bvp4c()来处理常微分方程组的边界问题。该函数解决第 一类边界条件问题,即边界条件是: g( y(a), y(b)) = 0 其中 a 和 b 是求解区间的下界和上界,即要求解出 y 在区间[a b]上的值。 常微分方程组的边界问题与常微分方程组的初值问题的不同之处在于:常微分方程组的 初值问题总是有解的,然而常微分方程组的边界问题有时会出现无解的情况,有时会出现有 限个解,有时会出现无穷多个解。因此在解常微分方程组的边界问题时,不可或缺的一部分 –244– 第 6 章 数据分析 工作是提供猜测解。猜测解决定了解边界问题的算法性能,甚至算法是否成功。 在边界问题中,还经常出现附加的未知参数,其方程形式如下: 其边界条件为: dy = f (x, y,p) dx g( y(a), y(b), p) = 0 在这种情况下,边界条件必须充分,从而能够决定未知参数 p。 函数 bvp4c()的调用格式如下: • sol = bvp4c(odefun,bcfun,solinit),odefun 是描述常微分方程组的函数,其格式为 dydx = odefun(x,y),或者包含未知参数 dydx = odefun(x,y,parameters)。bcfun 是描述边界条件 的函数,其格式为 res = bcfun(ya,yb),或者包含未知参数 res = bcfun(ya,yb,parameters)。 solinit 是对方程解的猜测解,它是一个结构体,包含 x、y 和 parameters3 种属性。solinit 必须满足 solinit.x(1)=a 和 solint.x(end)=b,parameters 是对未知参数的一个猜测解。 solinit 可以由函数 bvpinit()得到; • sol = bvp4c(odefun,bcfun,solinit,options),使用 options 结构体来设定解法器的参数。 options 结构体可以由函数 bvpset()得到。 边界问题的猜测解可以由函数 solinit()得到,其调用格式如下: • solinit = bvpinit(x,yinit),x 是猜测解的自变量 x 取值,yinit 猜测解的因变量 y 取值。如 果需要在区间[a b]上求解方程,x 取 linspace(a,b,10)就足够了,在解变化比较快的时候, 需要用长的自变量 x; • solinit = bvpinit(x,yinit,parameters),parameters 未知参数的猜测解。 例如,求 Mathieu 方程的特征值。Mathieu 方程的形式为: y ’’+ (l - 2q cos 2x) y = 0 其中 q-1 是 Mathieu 方程的阶数,为已知参数。λ是未知参数 Mathieu 方程的特征值。 为在本例中 q=5,即求 4 阶 Mathieu 方程的特征值λ。首先把方程改写成一阶常微分方程的 形式: 指定边界条件为: y ’1 = y2 y ’2 = - (l - 2q cos 2x) y1 y ’(0) = 0 y ’(p ) = 0 y(0) = 1 其次,创建 M 文件来描述 Mathieu 方程,代码设置如下: %bvp_Mathieufun.m %4 阶 Mathieu 方程 function dydx=bvp_Mathieufun(x,y,lambda) –245– MATLAB 7.0 q = 5; dydx = zeros(2,1); dydx(1) = y(2); dydx(2) = -(lambda - 2*q*cos(2*x))*y(1); 然后,创建 M 文件来描述方程的初值,代码设置如下: %Mathieu_initfun.m %4 阶 Mathieu 方程的初始值 function yinit = Mathieu_initfun(x) yinit(1) = cos(4*x); yinit(2) = -4*sin(4*x); 还需要,创建 M 文件来描述方程的边界条件,代码设置如下: %Mathieu_bcfun.m %4 阶 Mathieu 方程的边界条件 function res = Mathieu_bcfun(ya,yb,lambda) res = zeros(3,1); res(1) = ya(2); res(2) = yb(2); res(3) = ya(1)-1; 最后,用函数 bvp4c 来解方程,并画图显示结果,代码设置如下: %bvp4c_example.m %Mathieu 方程在边界条件下的解 lambda = 15; %未知参数猜测解 solinit = bvpinit(linspace(0,pi,10),@Mathieu_initfun,lambda);%求初始值 sol = bvp4c(@bvp_Mathieufun,@Mathieu_bcfun,solinit); %求解方程 xint = linspace(0,pi); %画图 Sxint = deval(sol,xint); plot(xint,Sxint(1,:)) axis([0 pi -1 1.1]) title(’Mathieu 方程在边界条件下的解’); xlabel(’x’) ylabel(’y’) text(0.1,1,[’4 阶 Mathieu 方程的特征值 = ’ num2str(sol.parameters)]); 由上述语句得到如图 6-33 所示的示意图。 –246– 第 6 章 数据分析 图 6-33 Mathieu 方程在边界条件下的解 –247– 第 7 章 Simulink 仿真环境 通过本章的学习,读者将获得以下知识: • 理解 Simulink 的概念及其应用; • 掌握如何使用 Simulink 搭建系统模型及其特点; • 掌握如何使用 Simulink 进行系统仿真并进行调试。 在学习本章时,读者既可按照作者介绍的先后顺序去逐步深入了解,也可以先通过第 7.2 节或第 7.11 节的学习来掌握 Simulink 建模的大概过程和基本步骤,然后再通过学习其他小节 的内容来掌握建模过程中的各种具体的操作细节和一些仿真计算的理论问题。 7.1 Simulink 概述 下面先向读者介绍一个非常简单的示例,旨在使读者在进行深入学习之前先对 Simulink 有一个感性的认识。 【创建模型目的】:计算两个不同频率正弦函数相加后再积分的结果,并显示结果的波形。 【创建模型的步骤】: • 在 MATLAB7.0 界面窗口选择 File→New→Model 菜单项,弹出如图 7-1 和图 7-2 所示 的窗口; • 在如图 7-1 所示窗口左边选中 Sources 库,然后在右边选 Sine Wave 模块并按住鼠标左 键不放,将它拖到如图 7-2 所示的窗口中。重复该操作添加第二个 Sine Wave 模块; 按上一步中的方法向图 7-2 的窗口中添加 Math Operations 库中的 Add 模块、Continuous 库中的 Integrator 模块和 Sinks 库中的 Scope 模块; 图 7-1 Simulink 模块库浏览器 图 7-2 新建模型窗口 第 7 章 Simulink 仿真环境 • 按如图 7-3 所示连接模块。连接模块的操作方法:用鼠标指向源模块的输出端口,当 鼠标变成十字架形时按住鼠标左键不放,然后拖动鼠标指向目标模块输入端口后松开; • 设置 Sine Wave 模块的参数。鼠标左键双击 Sine Wave 模块,弹出如图 7-4 所示参数设 置对话框,设置 Frequency 为 2,然后单击“OK”按钮; • 单击 按钮运行仿真,然后用鼠标左键双击模型中 Scope 模块,弹出如图 7-5 所示的输 出波形。 图 7-3 简单示例模型 图 7-4 设置参数 图 7-5 输出波形 –249– MATLAB 7.0 从入门到精通 7.1.1 Simulink 的概念 Simulink 是 MATLAB 提供的实现动态系统建模和仿真的一个软件包,它是 MATLAB 的 一个重要组成部分,而且具有相对独立的功能和使用方法。它支持线性和非线性、连续时间 系统、离散时间系统、连续和离散混合系统建模,且系统可以是多进程的。 Simulink 的一个突出特点是它支持图形用户界面(GUI),模型由模块组成的框图来表示。 用户建模通过简单的单击和拖动鼠标的动作就能完成。如果把建模比做建造房子,那么采用 高级语言或 MATLAB 语言直接编写仿真程序的方式来构建模型就好比是从一堆沙子开始来 建房子,这种方式不但麻烦容易出错,而且有许多重复操作,建造者的主要精力不是放在房 子的结构设计上,而是浪费在如何将沙子变成砖块以及如何将它们组合起来等技术性的问题 上,这显然不利于设计者去设计出更多有创造性的结构。在计算机仿真里,就等于设计者把 精力放在具体算法的实现上,而不是模型和算法设计本身。Simulink 通过自带的模块库(如 图 7-1 所示)为用户提供多种多样的基本功能模块,用户可以直接调用这些模块,而不必从 最基本的做起。这样就可以让设计者把精力放在更为重要的更具创造性的算法和模块结构的 设计上来。 Simulink 的每个模块对于用户来说都相当于一个“黑匣子”,用户只需知道模块的输入和 输出以及模块功能即可,而不必管模块内部是怎么实现的。因此,用户使用 Simulink 进行系 统建模的任务就是如何选择合适的模块并把他们按照自己的模型结构连接起来,最后进行调 试和仿真。如果仿真结果不满足要求,可以改变模块的相关参数再运行,直到结果满足要求 为止。至于在仿真时各个模块是如何执行的、各模块间是如何通信的、仿真的时间是如何采 样的以及事件是如何驱动的等细节问题,用户都不用去管,因为这些事情 Simulink 都解决了。 如何添加和删除模块、如何连接各个模块以及如何修改模块的参数和属性等问题将在本章后 面的各小节会陆续给予详细的介绍。 Simulink 最新版本是 Simulink6.0(包含在 MATLAB7.0 里),启动 Simulink 有 3 种方式。 • 在 MATLAB 7.0 的命令窗口直接键入“>>Simulink”命令; • 用鼠标左键单击 MATLAB 7.0 工具条上的 Simulink 按钮; • 在 MATLAB 7.0 菜单上选择“File→New→Model”选项。 运行后会弹出如图 7-1 所示的 Simulink 模块库浏览器窗口,使用第 3 种方式打开时还会 弹出如图 7-2 所示的新建模型窗口。单击图 7-1 工具条左边的图标 (建立新模型)也会弹 出如图 7-2 所示的窗口。 和 Windows 窗口类似,在 Simulink 的模块窗口和模块库窗口的“View”菜单下选择或 取消“Toolbar”和“Status Bar”选项,就可以显示或隐藏工具条和状态条。在进行仿真过程 中,模型窗口的状态条会显示仿真状态、仿真进度和仿真时间等相关信息。 注意:本章是以 Simulink 6.0 版为例进行介绍的。 7.1.2 Simulink 的工作环境 按照第 7.1.1 节介绍的方法启动 Simulink 后,就可以看到如图 7-1 所示的 Simulink 模块 库浏览器。在建模的过程中会经常对它进行操作,下面介绍该界面各个部分的用途,如图 7-6 –250– 所示。 第 7 章 Simulink 仿真环境 图 7-6 Simulink 模块库浏览器的结构 对于熟悉 Windows 窗口的读者来说,Simulink 模块库浏览器的界面应该很容易掌握,在 这里就不做详细介绍了。读者可以单击每个标题前的加号,将列出库里的模块,而且还能了 解到每个模块的功能说明,在第 7.1.6 小节将向读者详细地介绍其中一些库中的常用模块。 接下来向读者介绍 Simulink 模型窗口界面。模型建立好之后开始进行各项操作,大部分 的操作都是在模型窗口完成的,因此读者需要熟练掌握这些操作,并且要了解其中的各个菜 单(如表 7-1~表 7-6 所示)和按钮(如图 7-11 所示)的功能。 1.“File”菜单 “File”菜单中各选项的名称与功能如表 7-1 所示。 表 7-1 主要子菜单 New Open Close Save Save as Model Properties Preferences Source control Print Print Details Print Setup Exit MATLAB “File”菜单 功能 新建模型(Model)或库(Library) 打开一个模型 关闭模型 保存模型 另存为 打开“模型属性”对话框 打开“模型参数设置”对话框(如图 7-7 所示), “Preferences”对话框主要用于设 置一些用户界面的显示形式,如颜色、字体等 设置 Simulink 与 SCS 的接口 打印模型或模块图标到一个文件 生成 HTML 格式的模型报告文件,包括模块的图标和模块参数的设置等,如图 7-8 所示 打印模型或模块图标 退出 MATLAB 7.0 –251– MATLAB 7.0 从入门到精通 图 7-7 “Preferences”对话框 图 7-8 模型报告文件 2.“Edit”菜单 “Edit”菜单中各选项的名称与功能如表 7-2 所示。 表 7-2 主要子菜单 Copy Model to Clipboard Explore Block Properties Parameters Create Subsystem Mask Subsystem Look under Mask Signal Properties Edit Mask Subsystem Parameters Mask Parameters “Edit”菜单 功能 把模型当图片拷贝下来 打开模型浏览器,当有模块被选中时才可用 打开模块属性对话框,当有模块被选中时才可用 打开模块参数设置对话框,当有模块被选中时才可用 创建子系统,当有模块被选中时才可用 封装子系统,当有子系统被选中时才可用 查看子系统内部构成,当有子系统被选中时才可用 设置信号属性,当有信号被选中时才可用 编辑封装,当有子系统被选中时才可用 打开子系统参数设置对话框,当有子系统被选中时才可用 封装好的子系统的参数设置,当有被封装过的子系统被选中时才可用 3.“View”菜单 该菜单的内容较多,但大部分命令都很容易理解掌握。其中“Model Browser Options”、 “Block Data Tips Optioons”和“Port Values”用于设置在鼠标指针移到某一模块时有没有模块 及其端口的相关提示信息,菜单的子选项用于选择在提示信息中显示哪些内容。“View”菜 单中各主要选项具体的名称与功能如表 7-3 所示。 表 7-3 主要子菜单 Block Data Tips Optioons Library Browser Port Values Model Explorer “View”菜单 功能 用于设定在鼠标指针移到某一模块时是否显示模块的相关提示信息(如 模块名、模块参数名及其值和用户子定义描述字符串) 打开如图 7-1 所示的模型库浏览器 设置如何通过鼠标操作来显示模块端口的当前值 打开如图 7-9 所示的模型资源管理器,将模块的参数设置、仿真参数设 置以及解法器选择、模块的各种信息等集成到一个界面来设置 –252– 第 7 章 Simulink 仿真环境 图 7-9 模型资源管理器 4.“Simulation”菜单 “Simulation”菜单如图 7-10 所示,下面将介绍该菜单中各选项的功能。 “Start”:开始运行仿真。 “Stop”:停止仿真。 “Configuration Parameters”:设置仿真参数和选择解法器(设置方法在第 7.7.2 节介绍)。 “Normal”、“Accelerator”、“External”分别表示正常工作模式、加速仿真和外部工作模式。 图 7-10 “Simulation”菜单 5.“Format”菜单 该菜单主要用于设置字体、屏幕颜色、模块名的显示、模块显示颜色、信号和端口类型 和宽度等,各项都较容易掌握,各子菜单的功能如表 7-4 所示。 表 7-4 主要子菜单 Flip Name Flip Block Rotate Block Show Drop Shadow Port/Signal Displays Block Displays “Format”菜单 功能 翻转模块名字,当有模块被选中时才可用 翻转模块图标,当有模块被选中时才可用 旋转模块图标,当有模块被选中时才可用 给模块添加阴影,当有模块被选中时才可用 显示端口和信号的相关信息,其中“Sample Time Colors”选项根 据模块的采样时间来设置不同的显示颜色 显示模块相关信息,其中“Sorted Order”选项显示模块的优先级 –253– MATLAB 7.0 从入门到精通 6.“Tools”菜单 该菜单的内容较多,大部分是用来打开各种编辑器或设置对话框,用以设置与模型的仿 真相关的参数。“Tools”菜单中各主要选项的功能如表 7-5 所示。 表 7-5 主要子菜单 Simulink Debugger Fixed-Point Settings Model Advisor Lookup Table Editor Data Class Designer Bus Editor Profiler Coverage Settings Signal & Scope Manager Real-Time WorkShop External Mode Control Panel Control Design Parameter Estimation Report Generator “Tools”菜单 功能 打开调试器(关于调试器的使用在第 7.9 介绍) 打开定点设置对话框 打开模型分析器对话框,帮助用户检查和分析模型的配置 打开查表编辑器,帮助用户检查和修改模型中的 lookup table (LUT) 模 块的参数 打开数据类设计器,帮助用户创建 Simulink 类的子类,即创建自定义数 据类 打开 Bus 编辑器,帮助用户修改模型中 Bus 类型对象的属性 选中此菜单后,当仿真运行结束后会自动生成并弹出一个仿真报告文件 (HTML 格式) 打开“Coverage Settings” 对话框,用户可以通过该对话框设置在仿真 结束后给出仿真过程中有关 coverage data 的一个 HTML 格式报告文件 打开信号和示波器的管理器,帮助用户创建各种类型的信号生成模块和 示波器模块 可用于将模块转换为实时可执行的 C 代码 打开外部模式控制板,用于设置外部模式的各种特性 用于打开“Control and Estimation Tools Manager”和“Simulink Model Discretizer”对话框 用于打开“Control and Estimation Tools Manager”窗口,如图 7-11 所示, 可用于模型的参数分析 用于打开报告生成器 图 7-11 模型窗口的工具栏 7.“Help”菜单 “Help”菜单中各主要选项的名称与功能如表 7-6 所示。 表 7-6 主要子菜单 Using Simulink Blocks Blocksets Block Support Table Shortcuts S-Function Demos About Simulink –254– “Help”菜单 功能 打开 MATLAB 7.0 的帮助,当前显示在 Simulink 帮助部分 打开 MATLAB 7.0 的帮助,当前显示在按字母排序的 Blocks 帮助部分 打开按应用方向分类的帮助 打开模型所支持的数据类型帮助文件,如图 7-12 所示 打开 MATLAB 7.0 的帮助,当前显示在鼠标和键盘快捷键设置的帮助部分 打开 MATLAB 7.0 的帮助,当前显示在 S-函数的帮助部分 打开 MATLAB 7.0 的帮助,当前显示在 Demos 页的帮助部分,通过它可以 打开许多有用的演示示例 显示 Simulink 的版本 第 7 章 Simulink 仿真环境 图 7-12 模块支持的数据类型 7.1.3 Simulink 的工作原理 虽然 Simulink 为用户屏蔽了许多繁琐的编程工作,但用户要想更灵活高效地使用它,就 必须了解它的工作原理。由前面的示例可以看出 Simulink 建模大体可分成两步。创建模型的 图标和控制 Simulink 对它进行仿真。但是用户可能并不明白这些图形化的模型和现实系统之 间到底存在着怎样的映射关系,以及 Simulink 是如何对这些模型进行仿真的。下面就来解决 这两个问题。 1.图形化的模型和现实系统之间的映射关系 现实中的每一个系统都有输入、输出和状态 3 个基本元素,以及它们之间随时间变化的 数学函数关系。Simulink 模型中每一个图形化的模块代表现实系统中一个元件的输入、输出 和状态间随时间变化的函数关系,即元件或系统的数学模型,如图 7-13 所示。系统的数学模 型是由一系列数学方程式(代数环、微分和差分等式)来描述的,每一个模块都代表一组数 学方程,Simulink 中称这些方程为模块或模型的方法(即一组 MATLAB 函数)。模块和模块 之间的连线代表系统中各元件输入/输出信号的连接关系,也代表了随时间变化的信号值。 图 7-13 模块的图形化表示 2.Simulink 是如何对图形化模型进行仿真的 Simulink 对模型进行仿真的过程,就是在用户定义的时间段内根据模型提供的信息计算 系统的状态和输出的过程。当用户开始仿真,Simulink 分以下几个阶段来运行仿真。 –255– MATLAB 7.0 从入门到精通 (1)模型编译阶段 Simulink 引擎调用模型编译器,将模型编译成可执行的形式。编译器主要完成以下任务: • 评价模块参数的表达式以确定它们的值; • 确定信号属性(如名字、数据类型等); • 传递信号属性以确定未定义信号的属性; • 优化模块; • 展平模型的继承关系(如子系统); • 确定模块运行的优先级; • 确定模块的采样时间。 (2)连接阶段 Simulink 引擎创建按执行的次序排列的方法(也即函数)运行列表,同时定位和初始化 存储每个模块的运行信息的存储器。 (3)仿真环阶段 Simulink 引擎在从仿真的开始时间到结束时间,在每一个时间间隔点按顺序计算系统的 状态和输出。这些计算状态和输出的时间点就称作时间步,相邻两个时间点间的长度就称作 步长,步长的大小取决于求解器的类型。该阶段又分成两个子阶段。 • 仿真环初始化阶段。该阶段只运行一次,用于初始化系统的状态和输出。 • 仿真环迭代阶段。该阶段在定义的时间段内每隔一个时间步就重复运行一次,用于在 每一个时间步计算模型的新的输入、输出和状态,并更新模型使之能反应系统最新的 计算值。在仿真结束时,模型能反应系统最终的输入、输出和状态值。 7.1.4 Simulink 模型的特点 使用 Simulink 建立的模型具有以下 3 个特点: • 仿真结果的可视化; • 模型的层次性; • 可封装子系统。 下面通过一个 Simulink 提供的演示示例来说明上述特点。用户先通过菜单命令 Help→ MATLAB Help 或是“Help”命令打开如图 7-14 所示的帮助窗口,然后在“Demos”选项卡 中选择 Simulink→General Applications→Thermodynamic Model of a House,单击右边文本框中 有下画线的文字“Open this model”打开如图 7-15 所示的窗口。用户还可以通过在 MATLAB 7.0 命令窗口输入“>>thermo”命令打开该窗口。 • 单击“开始”按钮,可以看到如图 7-16 所示的仿真结果; • 鼠标左键双击模型图标中的 House 模块,弹出如图 7-17 所示 House 子系统图标。 对于上述的特点,读者在学完后续章节后将会有更加深刻的理解。 –256– 第 7 章 Simulink 仿真环境 图 7-14 MATLAB 7.0 帮助 图 7-15 thermo 演示模型 图 7-16 仿真结果可视化 图 7-17 House 子系统图标 7.1.5 Simulink 里的数据类型 在计算机编程语言中,数据类型决定了分配给一个数据的存储资源,决定数据表示的精 度、动态范围、性能和存储资源。MATLAB 语言也是一种编程语言,因此 Simulink 中也允 许用户说明 Simulink 模型中信号和模块参数的数据类型。 Simulink 在开始仿真之前以及仿真过程中会进行一个额外的检查(系统自动进行,不需 手动设置),以确认模型的类型安全性。所谓模型的类型安全性,是指保证该模型产生的代码 不会出现上溢或下溢,不至于产生不精确的运行结果。使用 Simulink 默认数据类型(double) 的模型都是固有类型安全的。 1.Simulink 支持的数据类型 Simulink 支持所有的 MATLAB 内置数据类型,内置数据类型是指 MATLAB 自己定义的 数据类型。表 7-7 列出了所有的 MATLAB 内置数据类型。 –257– MATLAB 7.0 从入门到精通 表 7-7 数据类型 MATLAB 7.0 内置数据类型 类型说明 Double Single Int8 Uint8 Int16 Uint16 Int32 Uint32 双精度浮点类型 单精度浮点类型 有符号 8 位整数 无符号 8 位整数 有符号 16 位整数 无符号 16 位整数 有符号 32 位整数 无符号 32 位整数 除了内置数据类型,Simulink 还定义了布尔类型,取值为 0 和 1,它们内部的表示是 uint8 (无符号 8 位整数)。所有的 Simulink 模块都默认为 double 类型的数据,但有些模块需要布尔 类型的输入,而另外一些模块支持多数据类型输入,还有些支持复数信号。 关于模块的输入/输出信号支持的数据类型的详细说明,用户可以通过模块参数设置对话 框(双击模块图标就会弹出对话框)来查看。如果在一个模块的参数设置对话框中没有说明 它支持的数据类型的选项,那就表示它只支持 double 类型的数据,如图 7-18 所示。用户还 可以选择 Simulink 模型窗口中的“Help”菜单中的“Block Support Table”子菜单,打开如图 7-12 所示的帮助窗口,其中总结了所有 Simulink 库中的模块所支持的数据类型的情况。 选择模型窗口的 Format→Port/Signal Displays→Port Data Types 可以查看信号的数据类型 和模块输入/输出端口的数据类型,如图 7-19 所示。 图 7-18 查看模块支持的数据类型 –258– 图 7-19 查看信号的数据类型 第 7 章 Simulink 仿真环境 在设置模块参数时,指定一个值为某一数据类型的方法为 type(value)。例如要把常数 模块的参数设为 1.0 单精度表示,则可以在参数对话框输入 single(1.0)。如果模块不支持所 设置的数据类型,那么就会弹出错误警告,如图 7-20 所示。 图 7-20 设置模块参数的数据类型 在建立模型时,常常需要向模型提供某种特定类型的输入信号数据,把一个具有特定数 据类型的信号引入模型具体有以下几种方法。 • 通过根目录 import 或者 FromWorkspace 模块将 MATLAB 7.0 工作空间中具有指定数据 类型的信号数据导入你的模型; • 建立一个常数模块并设置它的参数为指定的类型; • 使用 DataTypeConversion 模块将一个信号转换为指定类型的信号。 2.数据类型的传播 构造模型时会将各种不同类型的模块连接起来,而这些不同类型的模块所支持的数据类 型往往不完全相同。如果用直线连接在一起的两个模块所支持的数据类型(注意是指输出/ 输入信号的数据类型,而不是模块参数的数据类型)有冲突,那么当进行仿真、查看端口数 据类型或是更新数据类型时就会弹出一个提示对话框,告诉用户出现冲突的信号和端口,而 且有冲突的信号的路径会被加亮显示。这时就可以通过在有冲突的模块之间插入一个 DataTypeConversion 模块来解决类型冲突。 一个模块的输出一般是模块输入和模块参数的函数。而在实际建模过程中,输入信号的 数据类型和模块参数的数据类型往往是不同的,Simulink 在计算这种输出时会把参数类型转 换到信号的数据类型。但是如果出现下面的这些情形,就要进行不同的处理。 • 当信号的数据类型无法表示参数值时,Simulink 将中断仿真,并给出错误信息。 在如图 7-21 所示的示例模型,两个常数模块的输出信号类型设置为布尔型,而连续信号 积分器只接受 double 类型信号,所以弹出出错提示框。这时可以在示例模型中插入(如果读 者不熟悉该操作,参考第 7.2.1 节的内容)一个 DataTypeConversion 模块,并将其输出改成 double 数据类型就不会报错了,如图 7-22 所示。 –259– MATLAB 7.0 从入门到精通 图 7-21 数据类型示例模型 图 7-22 修改后的示例 • 如果信号的数据类型能够表示参数的值,仅仅是损失表示的精度,那么 Simulink 会继 续仿真,并在 MATLAB 7.0 命令窗口中给出一个警告信息。 在如图 7-23 所示的图中,常数模块的常数值参数设置为 uint8(1),输出数据类型设为 “Inherit from Constant value”;增益模块的增益参数设置为 double(3.2),输出数据类型设为 “Same as input”,参数数据类型设置为“Interit via internal rule”。Simulink 会把 3.2 的整数部 分截取,并转换成无符号数,所以最后结果为 3。 图 7-23 数据精度损失的示例 3.使用复数信号 Simulink 中默认的信号值都是实数,但在实际问题中有时需要处理复数信号。在 Simulink 中通常用下面示例中的两种方法来建立处理复数信号的模型,如图 7-24 所示。 • 向模型中加入一个 Constant 模块,将其参数设为复数; • 分别生成复数的虚部和实部,再用 Real-Image to Complex 模块把它们联合成一个复数; • 分别生成复数的幅值和幅角,再用 Magnitue-Angle to Complex 模块把它们联合成一个 –260– 复数。 第 7 章 Simulink 仿真环境 图 7-24 引入复数 同样可以在 Simulink 中利用相关的模块将一个复数分解成虚部和实部或是幅值和幅角, 而且 Simulink 中有许多的模块都接受复数输入并对复数进行运算,请读者自己查阅帮助文件 来了解这些模块。 注意:Real-Image to Complex 模块和 Magnitue-Angle to Complex 模块均在 Simulink 库的 Math Operations 子库中。 4.标量信号和向量信号 向量信号是多个信号的集合,对应着模块窗口中几条并行连线的合成。Simulink 中大多 数模块的输出默认情况下为标量信号,当输入为向量信号而模块参数为标量时,Simulink 会 自动进行匹配。接下来通过介绍两个示例来回答下面两个问题。 • 怎样让模块输出向量信号? • 当一个模块的输入既有向量又有标量时,Simulink 如何计算输出?即标量扩展问题。 【示例 1】使模块输出向量信号 在如图 7-25 所示的左边第一个示例,第一个模型中 Constant 模块的常数值参数设为[1 3], 输出信号的宽度为 2,因此 Scope 有两条直线。第二个模型中 Sine Wave 模块的幅度设为[1 3], 频率设为[1 2],输出一个两位的向量信号,因此 Scope 中有两条直线。 图 7-25 输出向量的模块示例 注意示例中 Sine Wave 的输出不是 4 位向量,模块中所有参数都是按向量元素的位置来 –261– MATLAB 7.0 从入门到精通 和信号对应,不存在自由组合的问题。读者在设置向量参数时一定要注意不同参数之间向量 大小的匹配问题。上例中若将 Frequency 设为 1,不会出错;但若是设为[1 2 3],就会报错。 【示例 2】既有向量又有标量输入的模块 在如图 7-26 所示的第二个示例中,由于输出信号的宽度设置为 2,因此 Scope 中显示有 两条直线。Simulink 将 Constant 的 1 位标量信号扩展成 2 位的向量,新扩展位的值与原来的 标量信号值相同。但是与上一例相似的是,如果一个信号是 3 位向量,一个是 2 位的向量, 那么就会报错,因为 Simulink 无法确定扩展 2 位向量为 3 位向量时该以 2 位向量哪一位的值 为标准。模块的参数也存在标量扩展的问题,读者只要多实践就会很容易掌握其中的规律。 图 7-26 标量扩展的示例 7.1.6 Simulink 里的模块和模块库 用 Simulink 建模的过程,可以简单地理解为从模块库里选择合适的模块,然后将它们连 接在一起,最后进行调试仿真。 模块库的作用就是提供各种基本模块,并将它们按应用领域以及功能进行分类管理以方 便用户查找。库浏览器将各种模块库按树状结构进行罗列,以便用户快速地查询所需模块, 同时它还提供了按名称查找的功能(如图 7-6 所示)。库浏览器中模块的多少取决于用户的安 装,但至少应该有 Simulink 库,用户还可以自定义库。 模块是 Simulink 建模的基本元素,了解各个模块的作用是熟练掌握 Simulink 的基础。库 中各个模块的功能可以在库浏览器中查到,如图 7-6 所示。下面详细介绍 Simulink 库中几个 常用子库中的常用模块的功能,如表 7-8~表 7-17 所示,完整的介绍见本书附录。 表 7-8 模块名 Bus Creator Bus Selector Constant Data Type Conversion Demux Discrete-Time Integrator Gain In1 Integrator Logical Operator –262– Commonly Used Blocks 子库 功能 将输入信号合并成向量信号 将输入向量分解成多个信号,输入只接受从 Mux 和 Bus Creator 输出的信号 输出常量信号 数据类型的转换 将输入向量转换成标量或更小的标量 离散积分器模块 增益模块 输入模块 连续积分器模块 逻辑运算模块 模块名 Mux Out1 Product Relational Operator Saturation Scope Subsystem Sum Switch Terminator Unit Delay 第 7 章 Simulink 仿真环境 功能 将输入的向量、标量或矩阵信号合成 输出模块 乘法器,执行标量、向量或矩阵的乘法 关系运算,输出布尔类型数据 定义输入信号的最大和最小值 输出示波器 创建子系统 加法器 选择器,根据第二个输入信号来选择输出第一个还是第三个信号 终止输出,用于防止模型最后的输出端没有接任何模块时报错 单位时间延迟 续表 表 7-9 模块名 Derivative Integrator State-Space Transport Delay Transfer Fcn Variable Transport Delay Zero-Pole Continuous 子库 功能 数值微分 积分器与 Commonly Used Blocks 子库中的同名模块一样 创建状态空间模型 dx/dt = Ax + Bu y = Cx + Du 定义传输延迟,如果将延迟设置得比仿真步长大,可以得到更精确的结果 用矩阵形式描述的传输函数 定义传输延迟,第一个输入接收输入;第二个输入接收延迟时间 用矩阵描述系统零点,用向量描述系统极点和增益 表 7-10 模块名 Coulomb& Viscous Friction Dead Zone Dead Zone Dynamic Hit Crossing Quantizer Rate Limiter Rate Limiter Dynamic Relay Saturation Saturation Dynamic Wrap To Zero Discontinuities 子库 功能 刻画在零点的不连续性,y = sign(x) * (Gain * abs(x) + Offset) 产生死区,当输入在某一范围取值时输出为 0 产生死区,当输入在某一范围取值时输出为 0,与 Dead Zone 不同的是 它的死区范围在仿真过程中是可变的 检测输入是上升经过某一值还是下降经过这一值或是固定在某一值, 用于过零检测 按相同的间隔离散输入 限制输入的上升和下降速率在某一范围 限制输入的上升和下降速率在某一范围,与 Rate Limiter 不同的是它的 范围在仿真过程中是可变的 判断输入与某两阈值的大小关系,当大于开启阈值,输出为 on;当小 于关闭阈值时,输出为 off;当在两者之间时输出不变 限制输入在最大和最小范围之内 限制输入在最大和最小范围之内,与 Saturation 不同的是它的范围在仿 真过程之中是可变的 当输入大于某一值时输出 0,否则输出等于输入 表 7-11 模块名 Difference Discrete Derivative Discrete Filter Discrete 子库 功能 离散差分,输出当前值减去前一时刻的值 离散偏微分 离散滤波器 –263– MATLAB 7.0 从入门到精通 模块名 Discrete State-Space Discrete Transfer Fcn Discrete Zero-Pole Discrete-Time Integrator First-Order Hold Integer Delay Memory Transfer Fcn First Order Zero-Order Hold 表 7-12 模块名 Bit Clear Bit Set Bitwise Operator Combinatorial Logic Compare To Constant Compare To Zero Detect Change Detect Decrease Detect Fall Negative Detect Fall Nonpositive Detect Increase Detect Rise Nonnegative Detect Rise Positive Extract Bits Interval Test Logical Operator Relational Operator Shift Arithmetic 表 7-13 模块名 Abs Add Algebraic Constraint Assignment Bias Complex to Magnitude-Angle Complex to Real-Imag Divide Dot Product Gain Magnitude-Angle to Complex Math Function –264– 功能 创建离散状态空间模型 x(n+1) = Ax(n) + Bu(n) y(n) = Cx(n) + Du(n) 离散传输函数 离散零极点 离散积分器 一阶保持 整数倍采样周期的延迟 存储单元,当前输出是前一时刻的输入 一阶传输函数,单位的直流增益 零阶保持 续表 Logic and Bit Operations 子库 功能 将向量信号中某一位置为 0 将向量信号中某一位置为 1 对输入信号进行自定义的逻辑运算 组合逻辑,实现一个真值表 定义如何与常数进行比较 定义如何与零进行比较 检测输入的变化,如果输入的当前值与前一时刻的值不等,则输出 TRUE,否则为 FALSE 检测输入是否下降,是则输出 TRUE,否则输出 FALSE 若输入当前值是负数,前一时刻值为非负则输出 TRUE,否则为 FALSE 若输入当前值是非正,前一时刻值为正数则输出 TRUE,否则为 FALSE 检测输入是否上升,是则输出 TRUE,否则输出 FALSE 若输入当前值是非负,前一时刻值为负数则输出 TRUE,否则为 FALSE 若输入当前值是正数,前一时刻值为非正则输出 TRUE,否则为 FALSE 从输入中提取某几位输出 检测输入是否在某两个值之间,是则输出 TRUE,否则输出 FALSE 逻辑运算 关系运算 算术平移 Math Operations 子库 功能 求绝对值 加法运算 将输入约束为零,主要用于代数等式的建模 选择输出输入的某些值 将输入加一个偏移,Y = U + Bias 将输入的复数转换成幅度和幅角 将输入的复数转换成实部和虚部 实现除法或乘法 点乘 增益,实现点乘或普通乘法 将输入的幅度和幅角合成复数 实现数学函数运算 模块名 Matrix Concatenation MinMax Polynomial MinMax Running Resettable Product of Elements Real-Imag to Complex Reshape Rounding Function Sign Sine Wave Function Slider Gain Subtract Sum Sum of Elements Trigonometric Function Unary Minus Weighted Sample Time Math 第 7 章 Simulink 仿真环境 续表 功能 实现矩阵的串联 将输入的最小或最大值输出 多项式求值,多项式的系数以数组的形式定义 将输入的最小或最大值输出,当有重置信号 R 输入时,输出被重置为初 始值 将所有输入实现连乘 将输入的两个数当成一个复数的实部和虚部合成一个复数 改变输入信号的维数 将输入的整数部分输出 判断输入的符号,若为正输出 1,为负输出-1,为零输出 0 产生一个正弦函数 可变增益 实现加法或减法 加法或减法 实现输入信号所有元素的和 实现三角函数和双曲线函数 一元的求负 根据采样时间实现输入的加法、减法、乘法和除法,只对离散信号适用 表 7-14 模块名 Configurable Subsystem Atomic Subsystem CodeReuseSubsystem Enable Enabled and Triggered Subsystem Enabled Subsystem For Iterator Subsystem Function-Call Generator Function-Call Subsystem If If Action Subsystem Model Subsystem Subsystem Examples Switch Case Switch Case Action Subsystem Trigger Triggered Subsystem While Iterator Subsystem Ports & Subsystems 子库 功能 用于配置用户自建模型库,只在库文件中才可用 只包括输入/输出模块的子系统模板 只包括输入/输出模块的子系统模板 使能模块,只能用在子系统模块中 包括使能和边沿触发模块的子系统模板 包括使能模块的子系统模板 循环子系统模板 实现循环运算模板 包括输入/输出和函数调用触发模块的子系统模板 条件执行子系统模板,只在子系统模块中可用 由 If 模块触发的子系统模板 定义模型名字的模块 只包括输入/输出模块的子系统模板 子系统演示模块,在模型中用鼠标左键双击该模块图标可以看 到多个子系统示例 条件选择模块 由 Switch Case 模块触发的子系统模板 触发模块,只在子系统模块中可用 触发子系统模板 条件循环子系统模板 表 7-15 模块名 Display Floating Scope Stop Simulation To File Sinks 子库 功能 显示输入数值的模块 浮置示波器,由用户来设置所要显示的数据 当输入不为零时,停止仿真 将输入和时间写入 MAT 文件 –265– MATLAB 7.0 从入门到精通 模块名 To Workspace XY Graph 表 7-16 模块名 Band-Limited White Noise Chirp Signal Clock Constant Counter Free-Running Counter Limited Digital Clock From File From Workspace Pulse Generator Ramp Random Number Repeating Sequence Signal Builder Signal Generator Sine Wave Step Uniform Random Number 表 7-17 模块名 Fcn Embedded MATLAB Function M-file S-Function MATLAB Fcn S-Function S-Function Builder S-Function Examples 续表 功能 将输入和时间写入 MATLAB 7.0 工作空间中的数组或结构中 将输入分别当成 X、Y 轴数据绘制成二维图形 Sources 子库 功能 有限带宽的白噪声 产生 Chirp 信号 输出当前仿真时间 输出常数 自动计数器,发生溢出后又从 0 开始 有限计数器,当计数到某一值后又从 0 开始 以数字形式显示当前的仿真时间 从 MAT 文件中读取数据 从 MATLAB7.0 工作空间读取数据 产生脉冲信号 产生按某一斜率的数据 产生随机数 重复输出某一数据序列 具有 GUI 界面的信号生成器,在模型中用鼠标左键双击模块图标可看到 如图 7-27 所示的图形用户界面,利用该界面可以直观地构造各种信号 信号产生器 产生正弦信号 产生阶跃信号 按某一分布在某一范围生成随机数 User-Defined Functions 子库 功能 简单的 MATLAB 7.0 函数表达式模块 内置 MATLAB 7.0 函数模块,在模型窗口用鼠标左键双击该模块图标就 会弹出 M 文件编辑器 用户使用 MATLAB 语言编写的 S-函数模块 对输入进行简单的 MATLAB 函数运算 用户按照 S-函数的规则自定义的模块,用户可以使用多种语言进行编写 具有 GUI 界面的 S-函数编辑器,在模型中用鼠标左键双击该模块图标 可看到如图 7-28 所示的图形用户界面,利用该界面可以方便地编辑 S函数模块 S-函数演示模块,在模型中用鼠标左键双击该模块图标可以看到多个 S函数示例 除 Simulink 库之外,MATLAB 7.0 还为用户提供了大量不同应用领域的基本模块库。关 于这部分内容,请读者通过前面介绍的方法在 Simulink 模块库浏览器中查看各个模块的功能。 –266– 第 7 章 Simulink 仿真环境 图 7-27 信号产生器图形用户界面 7.2 模型的创建 图 7-28 S-函数编辑器 7.2.1 Simulink 模块的基本操作 模块是建立 Simulink 模型的基本单元。利用 Simulink 进行系统建模就是用适当的方式把 各种模块连接在一起。下面如表 7-18 和表 7-19 所示的这些表格汇总了 Simulink 里对模块、 直线和信号标签进行各种操作的方法,其中有许多操作并不是惟一的,读者自己实践一下就 可以摸索出其他的操作方法。 表 7-18 任务 选择一个模块 选择多个模块 不同窗口间复制模块 同一模型窗口内复制模块 移动模块 删除模块 连接模块 断开模块间的连接 改变模块大小 调整模块的方向 给模块加阴影 修改模块名 模块名的显示与否 对模块进行操作 Microsoft Windows 环境下的操作 用鼠标左键单击要选择的模块,当用户选择了一个模块,那么之前选择的 模块被放弃 按住鼠标左键不放拖动鼠标,将要选择的模块包括在鼠标画出的方框里; 或者按住 Shift,然后逐个选择 直接将模块从一个窗口拖动到另一个窗口 先选中模块,然后同时按下 Ctrl+C 键,再同时按下 Ctrl+V 键;还可以在 选中模块后,通过快捷菜单来实现 按下鼠标左键直接拖动模块 先选中模块,再按下 Delete 键或者通过 Delete 菜单 先选中源模块,然后按住 Ctrl 并用鼠标左键单击目标模块 先按下 Shift 键,然后用鼠标左键拖动模块到另一个位置;或者也可以将 鼠标指向连线的箭头处,当出现一个小圆圈圈住箭头时按下鼠标左键并移 动连线 先选中模块,然后鼠标移到模块方框的一角,当鼠标图标变成两端有箭头 的线段时,按下鼠标左键拖动模块图标以改变图标大小 先选中模块,然后通过 Format→Rotate Block 菜单来改变模块方向 先选中模块,然后通过 Format→ Show Drop Shadow 菜单来改变模块方向 用鼠标左键双击模块名,然后修改 先选中模块,然后通过 Format→ ShowName/Hide Name 菜单来决定是否 显示模块名 –267– MATLAB 7.0 从入门到精通 任务 改变模块名的位置 在连线之间插入模块 续表 Microsoft Windows 环境下的操作 先选中模块,然后通过 Format→ Flip Name 菜单来改变模块名的显示位置 用鼠标拖动模块到连线上,使得模块的输入/输出端口对准连线 表 7-19 任务 选择多条直线 选择一条直线 连线的分支 移动直线段 移动直线顶点 直线调整为斜线段 直线调整为折线段 对直线进行操作 Microsoft Windows 环境下的操作 与选择多个模块的方法一样 鼠标左键单击要选择的连线,当用户选择一条连线,那么之前选择的连线被放弃 按下 Ctrl 键,然后拖动直线;或者按下鼠标左键并拖动直线 按下鼠标左键直接拖动直线 将鼠标指向连线的箭头处,当出现一个小圆圈圈住箭头时按下鼠标左键移动连线 先单击“Shift”按钮,将鼠标指向需要移动的直线上的一点并按下鼠标左键直接 拖动直线,如图 7-29 所示 按住鼠标左键不放直接拖动直线 图 7-29 对连线的操作 Simulink 中大部分模块的参数(Parameters)都允许用户进行设置。用户只需双击要设置 的模块或用鼠标右键单击模块并在弹出的上下文菜单中选择“ Parameters”或先 选中要设置的模块后再选择“Edit→ Parameters”就会弹出“参数设置”对话 框,如图 7-30 所示。 是对模块功能的描述,每个模块的参数设置对话 框都会有对自身功能描述的部分。图 7-30 中是一个增益模块,用户可以设置它的增益大小、 采样时间和输出数据类型等参数。模块参数还可以通过 set_param 命令设置,这在后面的章 节中再进行介绍。 图 7-30 模块参数设置对话框 注意:“ Parameters”中的是用户所选择的模块的名称。在图 7-30 的示 例中,用户所选择的模块是 Gain,所以上下文菜单中要选择的是“Gain Parameters”。 –268– 第 7 章 Simulink 仿真环境 Simulink 中的每个模块都有一个内容相同的属性(Properties)设置对话框,用鼠标右键 单击模块并在弹出的上下文菜单中选择“Block Properties”或先选中要设置的模块后再选择 “Edit→Block Properties”就会弹出属性设置对话框,如图 7-31 所示。“属性设置”对话框主 要包括三项内容。 • “General”页 【说明(Description)】 用于对该模块在模型中的用法进行注释。 【优先级(Priority)】 规定该模块在模型中相对于其他模块执行的优先顺序。优先级的数值必须是整数,如果 用户不输入数值,系统就会自动选取合适的优先级。优先级的数值越小(可以是负整数),优 先级越高。一般都不需要设置它。 【标记(Tag)】 用户为模块添加的文本格式的标记。 图 7-31 模块属性设置对话框 • “Block Annotation”页 用于指定在模块的图标下显示模块的哪个参数及其值,以什么格式显示。属性格式字符 串由任意的文本字符串加嵌入式参数名组成。首先设置“General”页的 Priority 为 4,然后在 “Block Annotation”页的“Enter text and tokens for annotation”文本框中输入“Priority= %\n Gain=%”,最后单击“Apply”,注意观察模块图标的变化,如图 7-32 所 示。 如果参数的值不是字符串或数字,相应位置会显示 N/S(not supported)。如果参数名无 效,该位置将显示“???”。 • “Callbacks”页 用于定义当该模块发生某种特殊行为时所要执行的 MATLAB 表达式,也称回调函数。 关于回调函数在后面的章节再进行介绍。 对信号进行标注以及在模型图表上建立描述模型功能的注释文字,是一个好的建模习 惯。信号标签和注释如图 7-33 所示。表 7-20 和表 7-21 列出了对标注和注释的具体操作方法。 给一个信号添加标签,只需用鼠标左键在直线上双击,然后输入文字即可。建立模型注释与 之类似,只要在模型窗口的空白处双击鼠标左键,然后输入注释文字即可。 –269– MATLAB 7.0 从入门到精通 图 7-32 设置“Block Annotation” 信号标注 图 7-33 注释和信号标注示例 表 7-20 任务 建立信号标签 复制信号标签 移动信号标签 编辑信号标签 删除信号标签 用粗线表示向量 显示数据类型 在连线上反映信息 Microsoft Windows 环境下的操作 在直线上直接用鼠标左键双击,然后输入 按下 Ctrl 键,然后按住鼠标左键选中标签并拖动 按住鼠标左键选中标签并拖动 在标签框内用鼠标左键双击,然后编辑 按下 Shift 键,然后用鼠标左键单击选中标签,再按 Delete 键 选择 Foamat→Port/Signal Displays→Wide Nonscalar Lines 菜单 选择 Foamat→Port/Signal Displays→Port Data Types 菜单 表 7-21 对注释进行处理 建立注释 复制注释 移动注释 编辑注释 删除注释 任务 Microsoft Windows 环境下的操作 在模型图标中用鼠标左键双击,然后输入文字 按下 Ctrl 键,然后按下鼠标左键选中注释文字并拖动 按下鼠标左键选中注释并拖动 单击注释文字,然后编辑 按下 Shift 键,然后用鼠标选中注释文字,再按 Delete 键 和模块类似,每一个模型和每一条连线(代表信号)都有“参数设置”对话框和“属性 设置”对话框,如图 7-34 所示(用鼠标右键单击模型的空白处选择相应的菜单就可以弹出对 –270– 话框),它们的作用将在后面的章节进行介绍。 第 7 章 Simulink 仿真环境 图 7-34 模型的仿真参数和“属性设置”对话框 7.2.2 创建模型的基本步骤 在本章的前面已向读者展示了一个简单的示例,通过该示例的学习,读者可能觉得使用 Simulink 建模实在是太简单了,只不过是用鼠标来选择几个模块,然后再把他们用几条线连 接起来,最后按一下运行菜单观察结果曲线就可以了!但是当再次遇到实际问题时,读者可 能会意识到所给的示例实在是一个太简单太过于理想化的模型,实际工程中要考虑的方面比 这些要多得多,而且也要复杂得多,因此读者需要进一步学习和掌握 Simulink 中更为深层的 内容。不过只要掌握了上一节的内容,读者就可以通过在线帮助来解决更为复杂的问题了。 在前面内容的基础上,下面向读者总结出使用 Simulink 进行系统建模和系统仿真的一般 步骤: • 画出系统草图。将所要仿真的系统根据功能划分成一个个小的子系统,然后用一个个 小的模块来搭建每个子系统。这一步骤也体现了用 Simulink 进行系统建模的层次性特 点。当然所选用的模块最好是 Simulink 的库里现有的模块,这样用户不必进行烦琐的 代码编写,当然这要求用户必须熟悉这些库的内容; • 启动 Simulink 模块库浏览器,新建一个空白模型; • 在库中找到所需模块并拖到空白模型窗口中,按系统草图的布局摆放好各模块并连接 各模块; • 如果系统较复杂、模块太多,可以将实现同一功能的模块封装成一个子系统,使系统 的模型看起来更简洁(封装的方法在第 7.3 节介绍); • 设置各模块的参数以及与仿真有关的各种参数(在后面的相关小节有详细介绍); • 保存模型,模型文件的后缀名为.mdl。 • 运行仿真,观察结果。如果仿真出错,请按弹出的错误提示框来查看出错的原因,然 后进行修改;如果仿真结果与预想的结果不符,首先是要检查模块的连接是否有误、 选择的模块是否合适,然后检查模块参数和仿真参数的设置是否合理。 • 调试模型。如果在上一步中没有检查出任何错误,那么就有必要进行调试(调试的方 法在第 7.9 节中介绍),以查看系统在每一个仿真步的运行情况,直至找到出现仿真结 –271– MATLAB 7.0 从入门到精通 果与预想的或实际情况不符的地方,修改后再进行仿真,直至结果符合要求。当然最 后还要保存模型。 7.2.3 模型文件格式 前面的示例模型都是通过图形界面来建立的,Simulink 还为用户提供了通过命令行建立 模型和设置模型参数的方法。一般情况下,用户不需要使用这种方式来建模,因为它要求用 户熟悉大量的命令,而且很不直观。这一节只对 Simulink 的模型文件格式作一个粗略的介绍, 用户若在实际建模时遇到相关的问题可以查阅在线帮助。 Simulink 将每一个模型(包括库)都保存在一个以.mdl 为后缀的文件里,称为模型文件。 一个模型文件就是一个结构化的 ASCII 文件,它包括关键字和各种参数的值。下面以一个示 例来介绍如何查看模型文件和模型文件的结构。 • 建立如图 7-35 所示的模型,并保存为 modelfile.mdl; • 在 MATLAB 7.0 窗口中通过“File→New→M-File”菜单打开 MATLAB Edit; • 在 MATLAB Edit 窗口中通过“File→Open”菜单打开刚刚保存的 modelfile.mdl 文件, 如图 7-35 所示。如果在目录中没有看到该文件,请查看文件打开对话框的文件类型是 否为所有文件。 由示例中的模型文件可以看出,模型文件按照模块继承的层次关系描述模型的各个组 件。文件的结构如下: Model { ... BlockDefaults { ... } AnnotationDefaults { ... } System { ... Block { ... } Line { ... –272– Branch { ... } } Annotation { ... } } } 第 7 章 Simulink 仿真环境 图 7-35 模型文件示例 文件分成下面几个部分来描述模型。 • Model 部分:用来描述模型参数,包括模型名称、模型版本和仿真参数等; • BlockDefaults 部分:用来描述模块参数的默认设置; • AnnotationDefaults 部分:用来描述模型的注释参数的默认值,这些参数值不能用 set_param 命令来修改; • System 部分:用来描述模型中每一个系统(包括顶层的系统和各级子系统)的参数。 每一个 System 部分都包括模块、连线和注释等。 7.3 子系统及其封装 7.3.1 创建子系统 1.为何创建子系统 当模型变得越来越大、越来越复杂时,用户很难轻易读懂以前所建的模型。在这种情况 –273– MATLAB 7.0 从入门到精通 下,通过子系统可以把大的模型分割成几个小的模型系统以使整个模型更简洁、可读性更高, 而且这种操作也不复杂。建立子系统有以下优点: • 减少模型窗口中模块的个数,使得模型窗口更加整洁; • 把一些功能相关的模块集成在一起,还可以实现复用; • 通过子系统可以实现模型图表的层次化,这样用户既可以采用自上而下的设计方法, 也可以采用自下而上的设计方法。 2.如何创建子系统 在 Simulink 中有两种创建子系统的方法。 • 通过子系统模块来创建子系统:先向模型中添加 Subsystem 模块,然后打开该模块并 向其中添加模块。 • 组合已存在的模块创建子系统:具体建立步骤见下一节的示例。 3.创建子系统示例 下面通过两个示例来介绍上文中提到的两种建立子系统的方法。 【示例 1】通过 Subsystem 模块来创建子系统 具体步骤如下: • 从 Ports&Subsystems 中复制 Subsystem 模块到自己的模型中,如图 7-36 所示; • 用鼠标左键双击 Subsystem 模块图标打开如图 7-36 所示 Subsystem 模块编辑窗口; • 在新的空白窗口创建子系统,然后保存; • 运行仿真并保存。 图 7-36 通过 Subsystem 模块创建子系统 【示例 2】组合已存在的模块创建子系统 具体步骤如下: • 创建如图 7-37 所示的系统; • 选中要创建成子系统的模块,如图 7-37 所示; • 选择【Edit-> Create Subsystem】菜单,结果如图 7-38 所示; • 运行仿真并保存。 –274– 第 7 章 Simulink 仿真环境 图 7-37 组合已存在的模块创建子系统 图 7-38 创建子系统示例 7.3.2 封装子系统 子系统可以使模型更简洁,但是在设置模型中各模块的参数时仍然很繁琐,此时可以使 用封装技术。封装可以使用户创建的子系统表现得与 Simulink 提供的模块一样拥有自己的图 标,并且用鼠标左键双击模块图标时会出现一个用户自定义的“参数设置”对话框,实现在 一个对话框中设置子系统中所有模块的参数。 1.为何封装 封装子系统可以为用户带来很多好处。 • 在设置子系统中各个模块的参数时只通过一个参数对话框就可以完成所需设置; • 为子系统创建一个可以反映子系统功能的图标; • 可以避免用户在无意中修改子系统中模块的参数。 2.如何封装 用户可以按照如下的步骤来封装一个子系统。 • 选择需要封装的子系统; • 选择“Edit→Edit mask”菜单,这时会弹出如图 7-39 所示的封装编辑器,通过它进行 各种设置。编辑器中各项参数的含义在下一节的示例中进行介绍; • 单击“Apply”或“OK”按钮保存设置。 图 7-39 封装编辑器 3.封装示例 封装的操作很简单,也比较容易理解,下面通过一个简单的示例来介绍如何进行封装。 –275– MATLAB 7.0 从入门到精通 • 建立如图 7-40 所示的含有一子系统的模型,并设置子系统中 Gain 模块的 Gain 参数为 一变量 m; • 选中模型中的 Subsystem 子系统,选择“Edit→Edit mask”菜单(或用鼠标右键单击 子系统弹出上下文菜单,选择“Edit mask”菜单)打开封装编辑器,如图 7-41 所示; • 进行封装设置。按照如图 7-41 所示设置【Icon 页】; 【Icon 页】允许用户定义封装子系统的图标,其中各项设置的含义如下: 【Icon options 面板】:定义图标的边框是否可见(Frame),系统在图标中自动生成的端口 标签等是否可见(Transparency.)等,用户只要试一试就会很快理解掌握。 【Drawing commands 文本框】:用 MATLAB 命令来定义如何绘制模型的图标,这里的绘 图命令可以调用【Initialization 页】中定义的变量。 【Examples of drawing commands 面板】:向用户解释如何使用各种绘制图标的命令,每种 命令都对应在右下角有一个示例。用户可以方便地按照“command”选项框中的命令格式和 右下角给出的相应示例图标来书写自己的图标绘制命令。 图 7-40 封装子系统示例 图 7-41 设置图标 • 按照如图 7-42 所示设置“Parameters”页 “Parameters”页允许用户定义封装子系统的参数对话框的可设置参数,其中各项设置 的含义如图 7-43 所示。 图 7-42 设置参数 图 7-43 参数设置的含义 • 按照如图 7-44 所示设置“Initialization”页 “Initialization”页允许用户定义封装子系统的初始化命令。初始化命令可以使用任何有 –276– 第 7 章 Simulink 仿真环境 效的 MATLAB 表达式、函数、运算符和在“Parameters”页定义的变量,但是初始化命令不 能访问 MATLAB 7.0 工作空间的变量。在每一条命令后用分号结束可以避免模型运行时在 MATLAB 7.0 命令窗口显示运行结果。一般在此定义附加变量、初始化变量或绘制图标等。 图 7-44 设置初始化参数 • 按照如图 7-45 所示设置“Documentation”页 “Documentation”页允许用户定义封装子系统的封装类型、模块描述和模块帮助信息, 其中各项设置的含义如图 7-46 所示。 图 7-45 设置“Documentation”页参数 图 7-46 “Documentation”设置的含义 单击“Apply”或“OK”按钮。用鼠标左键双击模型中的 Subsystem 子系统,弹出如图 7-47 所示封装子系统参数设置对话框。封装子系统的模块对话框中的变量不可在 MATLAB 工作空间赋值,这与非封装子系统不同。封装子系统有一个独立于 MATLAB 工作空间的内 部存储空间。 图 7-47 封装后的子系统的参数对话框 • 运行仿真,用鼠标左键双击模型中的 Scope 模块,看到如图 7-48 所示的结果。读者也 可以试着改变图 7-47 中 Gain 参数的值,然后观察输出是否有所改变,并考虑其原因。 –277– MATLAB 7.0 从入门到精通 图 7-48 示例模型仿真结果 注意:如果在保存上面示例的模型时弹出 error 提示框,有可能是用户的 MATLAB 7.0 或 simulink 在封装技术上不支持中文,这时需要将“Documentation”页中的中文说明改成英文。 7.3.3 定义自己的模块库 1.为何创建自己的模块库 当用户创建了很多封装子系统模块时,用户有必要分门别类地来存储这些模块。另外在 进行仿真建模时,为了减少到各种子库中来回查寻所需模块,用户有必要将同一类功能的一 组常用模块统一放置在同一模块库中。 2.如何创建自己的模块库 通过选择 Simulink 用户界面的“File→New→Library”菜单命令来创建模块库。选中该 命令后弹出一个空白的库窗口,然后将需要存放在同一模块库中的模块复制到模块库窗口中 即可,如图 7-49 所示。创建好模型后,用户在创建模型时可以不需要打开 Simulink 模块库浏 览器,而只需要在 MATLAB 7.0 命令窗口输入存放相应模块的模块库的文件名即可。 –278– 图 7-49 自建模型库 7.4 过零检测 第 7 章 Simulink 仿真环境 当仿真一个动态系统时,Simulink 在每一个时间步使用过零检测技术来检测系统状态变 量的间断点。如果 Simulink 在当前的时间步内检测到了不连续的点,那么它将找到发生不连 续的精确时间点,并且会在该时间点的前后增加附加的时间步。本节将向读者介绍过零检测 的重要性以及它的工作原理。 1.过零检测的重要性 在动态系统的运行过程中,状态的不连续经常会发生重要的事件,如当一个小球与地面 发生碰撞时,它的位置就会产生急剧的变化。不连续常常会导致动态系统的显著变化,因此 对不连续点进行精确的仿真非常重要,否则会导致仿真得到错误的系统行为。例如仿真碰撞 中的小球,如果小球与地面碰撞的时间点发生在仿真时间步内,模型中的小球会在半空中改 变方向,这会使研究者得到与物理学上的规律相违背的结论。 为了避免得到错误的结论,使不连续点发生的时刻成为仿真的一个时间点很重要。对于 一个纯粹靠求解器来决定仿真时间的仿真器很难有效地做到上面的要求。 固定步长的求解器在整数倍时间步长的时间点上计算状态变量的值,然而这并不能保证 有仿真时间点发生在状态不连续的时间点。当然用户可以通过减小时间步长使状态不连续的 时间点成为一个仿真时间点,但是这会减慢仿真速度。 可变步长的求解器可以保证有仿真时间点发生在状态不连续的时间点。它可以动态地改 变步长,当状态变量变化慢时增加步长,变化快时减小步长。在不连续点附近,系统状态发 生剧烈变化,因此理论上这种求解器可以精确地找到不连续发生的时间点,但是这会增加仿 真的时间点从而使仿真速度变慢。 2.过零检测的工作原理 使用过零检测技术,一个模块能够通过 Simulink 注册一系列过零变量,每一个变量就是 一个状态变量(含有不连续点)的函数。当相应的不连续发生时,过零函数从正值或负值传 递零值。在每一个仿真步结束时,Simulink 通过调用每一个注册了过零变量的模块来更新变 量。然后 Simulink 检测是否有变量的符号发生改变(相对于上一仿真时间点的结果),如果 有改变就说明当前时间步有不连续发生。 如果检测到过零点,Simulink 就会在每一个发生符号改变的变量的前一时刻值和当前值 之间内插入新值以评估过零点的个数,然后逐步增加内插点数目并使其值依次越过每一个过 零点。通过该技术,Simulink 避免精确地在不连续发生点进行仿真(不连续点处的状态变量 的值可能没有定义)。 过零检测使得 Simulink 可以精确地仿真不连续点而不必通过减小仿真步长增加仿真点来 实现,因此仿真速度不会受太大影响。大多数 Simulink 模块都支持过零检测,表 7-22 列出了 Simulink 中支持过零检测的模块。如果用户需要显式定义过零事件,可以使用 Discontinuities 子库中的 Hit Crossing 模块来实现。 –279– MATLAB 7.0 从入门到精通 表 7-22 持过零点检测的模块 模块名 Abs Backlash Dead Zone Hit Crossing Integrator MinMax Relay Relational Operator Saturation Sign Step Switch Subsystem 说明 一个过零检测:检测输入信号沿上升或下降方向通过零点 两个过零检测:一个检测是否超过上限阈值,一个检测是否超过下限阈值 两个过零检测:一个检测何时进入死区,一个检测何时离开死区 一个过零检测:检测输入何时通过阈值 若提供了 Reset 端口,就检测何时发生 Reset;若输出有限,则有三个过零检测即检 测何时达到上限饱和值、检测何时达到下限饱和值和检测何时离开饱和区 一个过零检测:对于输出向量的每一个元素,检测一个输入何时成为最大或最小值 一个过零检测:若 relay 是 off 状态,就检测开启点;若是 on 状态,就检测关闭点 一个过零检测:检测输出何时发生改变 两个过零检测:一个检测何时达到或离开上限,一个检测何时达到或离开下限 一个过零检测:检测输入何时通过零点 一个过零检测:检测阶跃发生时间 一个过零检测:检测开关条件何时满足 用于有条件地运行子系统:一个使能端口,一个触发端口 如果仿真的误差容忍度设置得太大,那么 Simulink 有可能检测不到过零点。例如,如果 在一个时间步内存在过零点,但是在时间步的开始和最终时刻没有检测到符号的改变(如图 7-50 所示),那么求解器将检测不到过零点。 图 7-50 过零点检测 7.5 代数环 1.何为代数环及代数环如何产生 有些 Simulink 模块的输入端口是支持直接馈入的,这意味着这些模块的输出信号值在不 知道输入端口的信号值之前就不能被计算出来。当一个支持直接馈入的输入端口由同一模块 的输出直接或间接地通过由其他模块组成的反馈回路的输出驱动时,就会产生一个代数环, 如图 7-51 所示。 图 7-51 代数环 从代数的角度来看,图 7-51 中的模块的解是 z = 2,但是大多数的代数环是无法直接看 出解的。Algebraic Constraint 模块为代数方程等式建模及定义其初始猜想值提供了方便,它 –280– 第 7 章 Simulink 仿真环境 约束输入信号 F(z)等于零并输出代数状态 Z,其输出必须能够通过反馈回路影响输入。用户 可以为代数环状态提供一个初始猜想值以提高求解代数环的效率。 一个标量代数环代表一个标量等式或是一个形如 F(z) = 0 的约束条件,其中 z 是环中一 个模块的输出,函数 F 由环路中的另一个反馈回路组成。可将图 7-51 所示的含有反馈环的模 型改写成用 Algebraic Constraint 模块创建的模型,其仿真结果不变,如图 7-52 所示。 图 7-52 Algebraic Constraint 模块创建代数环模块 创建向量代数环也很容易,在如图 7-53 所示的向量代数环可用下面的代数方程描述: z2 + z1 - 1 = 0 z2 - z1 - 1 = 0 图 7-53 向量代数环 当一个模型包含一个 Algebraic Constraint 模块时就会产生一个代数环,这种约束可能是 系统的物理连接的结果,或是用户试图为一个微分/代数系统(DAE)建模的结果。 2.如何求解代数环及如何消除代数环 为了求解 F(z) = 0,Simulink 环路求解器会采用弱线性搜索的秩为 1 的牛顿方法更新偏微 分 Jacobian 矩阵。尽管这种方法很有效,但是如果代数状态 Z 没有一个好的初始估计值,解 法器有可能不收敛。此时用户可以为代数环中的某个连线(对应一个信号)定义一个初始值, 设置的办法有两个:可以通过 Algebraic Constraint 模块的参数设置;还可以通过在连线上放 置 IC 模块(初始信号设置模块)。 当一个系统包含有代数环时,Simulink 会在每一个时间步进行循环求解。如果有可能, 环路求解器会采用迭代的办法来求解,因此仿真速度会比较慢。 当一个模型中含有 Atomic Subsystem、Enabled Subsystem 或 Model 模块时,Simulink 可 以通过模块的参数设置来消除其中一些代数环。对于含 Atomic Subsystem 和 Enabled Subsystem 模块的模型,可在模块参数设置对话框中选择“Minimize algebraic loop occurrences” 项;对于含 Model 模块的模型,可在“configuration parameters 对话框”的“Model Referencing 面板”中选择“inimize algebraic loop occurrences”项。 –281– MATLAB 7.0 从入门到精通 7.6 回调函数 在前面介绍“模块属性设置”对话框时提到过回调函数(如图 7-32 所示的“设置模块属 性”对话框)。所谓回调函数,是指当用户定义的模型或模块的图标发生某种特殊行为时所要 执行的 MATLAB 表达式(M 文件和 M 文件函数)。 为模型或模块设置回调函数的方法有下面两种: • 通过模型或模块的属性对话框来设置; • 通过 MATLAB 相关的命令来设置。 在如图 7-54 所示的“模块属性设置”对话框中的 Callbacks 页列出了可以为模块设置回 调函数的参数(每一个参数对应模块的一种行为),表 7-23 列出了这些参数的含义。可以为 模型设置回调函数的参数与可设置的模块参数类似,如表 7-24 所示。图 7-55 是“模型属性 设置”对话框。 图 7-54 为模块设置函数的参数 图 7-55 模型属性对话框 表 7-23 模块回调参数名称 ClipboardFcn CloseFcn CopyFcn DeleteFcn DeleteChildFcn DestroyFcn InitFcn LoadFcn ModelCloseFcn MoveFcn NameChangeFcn OpenFcn ParentCloseFcn PostSaveFcn PreSaveFcn StartFcn 模块的回调参数 参数含义 在模块被复制或是剪切到系统粘贴板时 当使用 close_system 命令关闭模块时 模块被复制之后,该回调对于子系统是递归的。如果是使用 add_block 命令 复制模块,该回调也会被执行 在模块被删除之前 在模块从子系统中被删除之后 当模块已经被毁坏时 在模块被编译和模块参数被估值之前 在模块被载入之后,该回调对于子系统是递归的 在模块被关闭之前,该回调对于子系统是递归的 在模块被移动或调整大小时 在模块的名字或路径发生改变时 在模块被打开时,一般用于子系统模块。当用户双击打开模块或者使用 open_system 命令打开模块时调用 在关闭包含该模块的子系统或者是用 new_system 命令建立的包含该模块的 子系统时 在模块被保存之后,该回调对于子系统是递归的 在模块被保存之前,该回调对于子系统是递归的 在模块被编译之后,仿真开始之前 –282– 第 7 章 Simulink 仿真环境 模块回调参数名称 StopFcn UndoDeleteFcn 参数含义 在仿真结束时 当一个模块的删除操作被取消时 续表 表 7-24 模型的回调参数 模型回调参数名称 CloseFcn PostLoadFcn InitFcn PostSaveFcn PreLoadFcn PreSaveFcn StartFcn StopFcn 参数含义 在模型图表被关之前调用 在模型被载入之后调用 在模型的仿真开始时调用 在模型被保存之后调用 在模型被载入之前调用,用于预先载入模型使用的变量 在模型被保存之前调用 在模型仿真开始之前 在模型仿真停止之后,在 StopFcn 执行前,仿真结果先被写入工作空间中的 变量和文件中 下面通过一个示例介绍为模型或模块设置回调函数的两种方法的具体实现过程。 【第一种方法】 • 首先建立一个如图 7-56 所示的模型 example; • 设置模型属性对话框“CallBack”页的 Simulation stop function 文本为 plot(tout,yout); • 运行仿真,结束时会自动弹出图形窗口。 同样也可以通过设置 Out1 模块的 Callback 属性来达到这一目的,如图 7-57 所示。 图 7-56 通过模型属性对话框为模型设置回调函数 【第二种方法】 图 7-57 通过模块属性对话框为模型设置回调函数 –283– MATLAB 7.0 从入门到精通 • 首先建立好如图 7-56 所示的模型; • 设置 Constant 模块参数为变量 con,Sine Wave 的幅度和频率参数设置为 3 位向量; • 在 MATLAB 7.0 命令窗口输入下面的代码: set_param(’example’,’PreLoadFcn’,’ini_con’); set_param(’example’,’StopFcn’,’out_plot’); • 保存模型之后,新建一个名为 ini_con 的 M 文件,其内容为 con = 3; • 再新建一个名为 out_plot 的 M 文件,其内容为 plot(tout, yout); • 运行仿真,结果如图 7-58 所示。 注意:如果读者在运行示例时出错,请检查你的当前目录是不是你保存模型的目录。如果不是, 请将当前目录改成保存模型的目录,再运行上面两条 set_param 命令,最后运行仿真。 图 7-58 通过 MATLAB 命令为模型设置回调函数 7.7 运行仿真 建好一个模型之后就要运行模型和分析仿真结果,前面也简单地提到过运行仿真,本节 将对启动仿真的方法及各种仿真参数的设置进行详尽地讲解。 7.7.1 使用窗口运行仿真 当建立好模型后,可以直接在模型窗口通过菜单或工具栏进行仿真,如图 7-59 所示。可 以通过这些菜单设置仿真的参数、仿真的时间以及选择解法器等,而不需要去记忆 MATLAB 的各种命令语法。前面第 7.1.2 节已对模型窗口的菜单和工具栏作了详尽介绍,读者加以实践 即可掌握。 –284– 图 7-59 通过菜单命令或工具栏按钮运行仿真 7.7.2 仿真参数的设置 第 7 章 Simulink 仿真环境 1.设置仿真参数和选择解法器 可以通过模型窗口的“Simulation→ Configuration Parameters”命令打开设置仿真参数的 对话框,也可以通过上下文菜单的“Configuration Parameters”项(单击模型窗口中的空白处 也可以打开)打开,如图 7-60 所示。 图 7-60 仿真参数对话框 对话框将参数分成不同类型的 6 组,下面对每一组中各个参数的作用和设置方法进行简 单的介绍。 (1)【Solver 面板】 该面板主要用于设置仿真开始和结束时间,选择解法器,并设置它的相关参数,如图 7-61 所示。 图 7-61 Solver 面板 Simulink 支持两类解法器:固定步长和可变步长解法器。两种解法器计算下一个仿真时 间的方法都是在当前仿真时间上加一个时间步长。不同的是,固定步长解法器的时间步长是 常数,而可变步长解法器的时间步长是根据模型动态特性可变的。当模型的状态变化特别快 时,为了保证精度要降低时间步长,反之就增加时间步长。面板中 Type 用于设置解法器的类 型,当选择了不同的类型时,Solver 中可选的解法器列表也不同,有关各种求解器的使用范 围请读者查阅帮助。 当用户希望通过自建模型生成代码并在实时计算系统中运行这些代码时,用户就应该选 择固定步长解法器来仿真模型。这是因为实时计算系统以固定的采样速率运行,若采用可变 步长将有可能使仿真发生错误。 –285– MATLAB 7.0 从入门到精通 当用户并不从模型生成代码时,解法器类型的选择决定于模型的动态特性。当模型的状 态变化特别快或是包含有不连续状态时,选择可变步长解法器可以缩短仿真时间。这是因为 可变步长相对于固定步长的解法器需要更少的时间步,而两者仿真的精度相当。 关于该面板中其他参数的设置,读者可以查看在线帮助,它们都比较容易理解。 (2)【Data Import/Export 面板】 该面板主要用于向 MATLAB 7.0 工作空间输出模型仿真结果数据,或从 MATLAB 7.0 工 作空间读入数据到模型,如图 7-62 所示。 【Load from workspace】:从 MATLAB 7.0 工作空间向模型导入数据,作为输入和系统的 初始状态。 【Save to workspace】:向 MATLAB 7.0 工作空间输出仿真时间、系统状态、系统输出和 系统最终状态。 【Save options】:向 MATLAB 7.0 工作空间输出数据的数据格式、数据量、存储数据的变 量名以及生成附加输出信号数据等。 图 7-62 Data Import/Export 面板 (3)【Optimization 面板】 该面板用于设置各种选项来提高仿真性能和由模型生成的代码的性能,如图 7-63 所示。 图 7-63 Optimization 面板 “Block reduction optimization”选项:设置用时钟同步模块来代替一组模块,以加速模型 的运行。 “Conditional input branch execution”选项:用于优化模型的仿真和代码的生成。 “Inline parameters”选项:选中该选项使得模型的所有参数在仿真过程中不可调,Simulink 在仿真时就会将那些输出仅决定于模块参数的模块从仿真环中移出,以加快仿真。如果用户 要 想 使 某 些 变 量 参 数 可 调 , 那 么 可 以 单 击 “ configure ” 按 钮 打 开 “ Model Parameter Configuration ”对话框将这些变量设置为全局变量。 –286– 第 7 章 Simulink 仿真环境 “Implement logic signals as boolean data (vs. double)”选项:使得接受布尔值输入的模块 只能接受布尔类型,若该项没被选,则接受布尔输入的模型也能接受 double 类型输入。 (4)【Diagnostics 面板】 该面板主要用于设置当模块在编译和仿真遇到突发情况时,Simulink 将采用哪种诊断动 作,如图 7-64 所示。该面板还将各种突发情况的出现原因分类列出,各类突发情况的诊断办 法的设置在此不作详细介绍。 图 7-64 Diagnostics 面板 (5)【Hardware Implementation 面板】 该面板(如图 7-65 所示)主要用于定义硬件的特性(包括硬件支持的字长等),这里的 硬件是指将来要用来运行模型的物理硬件。这些设置可以帮助用户在模型实际运行目标系统 (硬件)之前通过仿真检测到以后在目标系统上运行可能会出现的问题,如溢出问题等。 图 7-65 Hardware Implementation 面板 (6)【Model Referencing 面板】 该面板主要用于生成目标代码、建立仿真以及定义当此模型中包含其他模型或其他模型 引用该模型时的一些选项参数值,如图 7-66 所示。 图 7-66 Model Referencing 面板 –287– MATLAB 7.0 从入门到精通 ① 当前模型中含有其他模型时(Rebuild options for all referenced models 其他)的设置。 【Rebuild options】:用于设置是否要在当前模型更新、运行仿真和生成代码之前重建仿真 和 Real-Time Workshop 目标。因为在进行模型更新、运行仿真和生成代码时,有可能其中所 包含的其他模型发生了改变,所以需要在这里进行设置。 ② 其他模型中包含有当前模型时(Options for referencing this model 其他)的设置。 【Total number of instances allowed per top model】:用于设置在其他模型中可以引用多少 个该模型。 【Model dependencies】:用于定义存放初始化模型参数的命令以及为模型提供数据的文件 名(通常是 MAT 文件或 M 文件)或文件的路径,定义的方法是将文件名或文件路径的字符 串 定 义 成 字 符 串 细 胞 阵 列 , 如 {’D:\Work\parameters.mat’, ’$MDL\mdlvars.mat’, ... ’D:\Work\masks\*.m’}。 【Pass scalar root inputs by value】:选中此项后,别的模型在调用该模型时就会通过数值 来传递该模型的标量输入,否则就通过参考(如输入的地址)来传递输入。选中此项就会允 许模型从速度快的寄存器或局部存储单元读取数据,而不是从它的实际输入位置来读取。如 果模型的输入在一个时间步内发生改变,那么选中此项就会导致仿真出错。例如,当模型的 输入/输出共享同一个局部存储单元(代数环)并且在一个时间步被调用多次时,模型只有当 输入是通过参考传递时才会看到标量输入在同一时间步内发生的改变。 【Minimize algebraic loop occurrences】:选中此项后,Simulink 就试图消除模型中的一些 代数环。 注意:所有通过仿真参数对话框设置的参数均可以通过 sim 和 simset 命令来设置。 2.仿真诊断对话框 如果 Simulink 在仿真运行过程中遇到错误,那么 Simulink 将停止仿真,并弹出仿真诊断 对话框。通过该对话框,用户可以方便地在模型中找到出错的地方和出错的原因。将第 7.6 节中图 7-58 示例的 M 文件 ini_con 中的内容改为 con =[2 3]后再运行仿真,将弹出如图 7-67 所示的错误提示对话框。下面就介绍一下该对话框各部分提示的含义。 图 7-67 仿真诊断对话框 对话框分为上下两个面板,上面的面板列出了每一个错误的信息,这些信息包括: • Message:消息类型(模块错误、警告、log 等); • Source:导致错误的模型元素(如模块或连线等)的名称; • Reported by:报告错误的组件(如 simulink、Real-Time Workshop 等); • Summary:错误消息的简写,便于在列表中显示。 –288– 第 7 章 Simulink 仿真环境 下面的面板显示当前所选中的 Message 的完整内容,包括出错原因和出错的元素。当用 户选中某一个 Message 时,Simulink 就会打开模型窗口,并且将产生错误的模型元素用黄色 加亮显示。 7.7.3 使用 MATLAB 命令运行仿真 使用模型窗口直接进行系统仿真时,每一次运行都是针对某一固定的模块参数设置,在 运行过程中不能轻易地改变它。MATLAB 允许从命令行运行仿真,通过 MATLAB 命令进行 模型的仿真使得用户可以从 M 文件来运行仿真。这样就允许不断地改变模块参数并运行仿 真,也就让用户可以随机地改变参数和循环地运行仿真,用户就可以进行蒙特卡罗分析了。 MATLAB 7.0 提供了 sim 命令运行仿真,该命令完整的语法如下: [t,x,y] = sim(model, timespan, options, ut); 或者输入下面的代码: [t,x,y1, y2, ..., yn] = sim(model,timespan,options,ut); 其中只有 model 参数是必需的,其他的参量都被允许设置为空矩阵([])。Sim 命令中没 有设置的或是设为空矩阵的参数的值等于建立模型时通过模块参数对话框设置的值或是系统 默认的值;sim 命令中设置的参数值会覆盖模型建立时设置的参数值。如果你仿真的模型是 连续系统,那么命令中还必须通过 simset 命令设定 solver 参数,默认的 solver 参数是求解离 散模型的 VariableStepDiscrete。sim 中各参量的含义如表 7-25 所示。 表 7-25 sim 命令参量 参量名 T X Y Y1,…,yn model Timespan Options Ut 参量含义 返回仿真时间 返回仿真的状态矩阵 返回仿真输出矩阵 每一个 Yi 对应一个输出模块 模型名 设置仿真的开始和结束时间 用于设置仿真的相关参数的一个结构 模型输入 Simset 命令用于创建一个 options 结构,该结构中设定仿真参数和求解器的属性值。该命 令的语法如下: options = simset(property, value, ...); options = simset(old_opstruct, property, value, ...); options = simset(old_opstruct, new_opstruct); simset 由于一般情况下,用户不会用到该命令,在此不作详细介绍,如果碰到可以查阅在线帮 助,相关的命令还有 simplot、simget、set_param 等。用 sim 命令仿真第 7.6 节的示例,代码 如下: • 先在 MATLAB 7.0 命令窗口执行: set_param(’example’,’PreLoadFcn’,’ini_con’); set_param(’example’,’StopFcn’,’out_plot’); –289– MATLAB 7.0 从入门到精通 • 然后执行: [t,x,y]=sim(’example’) 使用 sim 命令运行仿真的结果如图 7-68 所示。 图 7-68 使用 sim 命令运行仿真 另外还可以通过 set_param 命令来开始、结束、暂停或者继续仿真,该命令的使用格式 如下: set_param(’sys’, ’SimulationCommand’, ’cmd’) 其中,’sys’ 是要仿真的系统的名称;’cmd’是控制命令,可以取值 start、stop、pause、continue 或 update。类似的命令有 get_param,它可用来检查仿真的状态,其使用格式是: get_param(’sys’, ’SimulationStatus’) 该命令的返回值是 stopped、initializing、running、paused、updating、terminating 或者 external。 7.7.4 改善仿真性能及精度 Simulink 的仿真性能和精度受许多因素的影响,包括模型的设计和仿真参数的设置。对 于大多数的问题,使用默认的仿真参数和解法器就可以得到满意的仿真结果。但是对于某些 模型,适当的调整仿真参数可以得到更好的结果。 1.加速仿真 如果一个模型的仿真速度过慢,可能造成的原因有以下几种。 • 模型中有 MATLAB Fcn 模块。如果一个模型中含有 MATLAB Fcn 模块,那么在仿真 时,Simulink 在每一个仿真时间步都要调用一次 MATLAB 解释器,所以应该尽可能 –290– 第 7 章 Simulink 仿真环境 的使用 Simulink 的内置 Fcn 模块或是 Math Function 模块。 • 模型中有 M 文件的 S-函数。M 文件的 S-函数也会使每一个仿真步调用 MATLAB 解释 器。这时应该将它转换成一个子系统或是 C-MEX 文件的 S-函数。 • 模型中含存储模块。使用存储模块将使阶数可变的解法器在每个时间步被重置为 1 阶。 • 仿真的时间步长的值太小。可将它设为默认值。 • 仿真精度设置要求太高。一般情况下,使用默认的 0.1%相对误差就可以。如果该值设 得太小,那么这会使仿真在接近零状态附近时耗费更多的仿真步。 • 仿真时间设得太长。可适当减小时间间隔。 • 模型所求解的问题是一个 stiff 问题,而用户选择的是一个非 stiff 求解器。可以试用 ode15s。 • 模型包含有代数环。代数环是通过迭代的方法进行求解的,故仿真速度会比较慢。 • 模型中把一个 Random Number 模块作为 Integrator 模块的输入。若用户建立的是一个 连续系统,可以使用 Sources 库中的 Band-Limited White Noise 模块作为 Integrator 模块 的输入。 • 模型的采样时间彼此之间不是整数倍,求解器会选择足够小的时间步长来确保所有设 置的采样点都能取到,因此会导致仿真速度下降。 2.改善仿真的精度 用户可以通过对比在不同的相对误差或绝对误差参数值时的仿真结果来判断解是否收 敛。如果仿真的结果变化不大,则说明收敛。用户可以通过设置较小的仿真步长来保证仿真 不会错过模型的关键行为。 如果仿真结果看起来不是很精确,那么有可能是下面的原因造成的。 • 模型含有取值接近零的状态。此时,如果绝对误差参数的值设置过大,那么仿真在接 近零状态时仿真的时间步就太少。用户可以通过设置较小的绝对误差参数来解决这个 问题。 • 如果减小绝对误差的办法还不能提高仿真精度,那么用户就应该减小相对误差参数的 值以降低可容忍的误差和减小仿真的步长。 7.8 仿真结果分析 在第 7.1.4 节中讲过,仿真结果的可视化是 Simulink 建模的一个特点。不仅如此,Simulink 还可以对仿真的结果进行分析。 7.8.1 观看输出结果 在 Simulink 中有 3 种方法绘制模型仿真的输出: • 在模型中将信号输入到 Scope 模块或是 XY Graph 模型; • 将输出写入变量,然后使用 MATLAB 绘图命令; • 将输出写入 To Workspace 模块,然后使用 MATLAB 绘图命令。 –291– MATLAB 7.0 从入门到精通 7.8.2 线性化 1.何为线性化 线性化就是将用户所建模型用状态空间矩阵形式 A、B、C 和 D 表示的线性方程模型。 状态空间矩阵按如下方程描述线性的输入/输出关系: x′ = Ax + Bu y = Cx + Du 其中,x,u,y 分别表示状态、输入和输出的向量。模型中的输入/输出必须使用 Simulink 提供的输入(In1)输出(Out1)模块。关于何为状态向量以及如何将一个系统用状态空间矩 阵来描述等内容,请读者参考与信号与系统相关的书籍。 2.为何线性化 一旦数据具有状态空间的形式或是被转化成 LTI 对象,用户就可以使用控制系统工具箱 里的函数作进一步的分析。 将状态空间转换为 LTI 对象: sys = ss(A,B,C,D) 绘制系统的波特相位幅频图: bode(A,B,C,D) 或 bode(sys) 求系统的时间响应: step(A,B,C,D) 或 step(sys):单位阶跃响应 impulse(A,B,C,D) 或 impulse(sys):单位脉冲响应 lsim(A,B,C,D,u,t) 或 lsim(sys,u,t):任意输入响应 3.如何线性化 用户可以通过下面几个函数对所建模型进行线性化: [A,B,C,D] = linmod(’sys’); [A,B,C,D] = linmod(’sys’,x,u); [A,B,C,D] = linmod(’sys’, x, u, para); [A,B,C,D] = linmod(’sys’, x, u, ’v5’, para); [A,B,C,D] = linmod(’sys’, x, u, ’v5’, para, xpert, upert); [A,B,C,D] = dlinmod(’sys’, x, u); [A,B,C,D] = dlinmod(’sys’,Ts, x, u, ’v5’, para); [A,B,C,D] = dlinmod(’sys’, x, u, ’v5’, para, xpert, upert); argout = linmod2(’sys’, x, u, para); linmod 用于线性化本身是线性和非线性的模型,dlinmod 用于线性化本身是离散系统或 者离散和连续的混合系统。以上函数的具体使用方法及其中各个参数的含义,用户可以查询 在线帮助。 –292– 7.8.3 平衡点的分析 第 7 章 Simulink 仿真环境 Simulink 通过 trim 命令来决定动态系统的稳定状态点,所谓稳定状态点就是满足用户自 定义的输入、输出和状态条件的点。下面通过一个简单的示例来介绍如何使用 trim 命令求解 平衡点。 • 建立如图 7-69 所示的模型,保存为“exampbalance”; • 为系统状态和输入定义一个初步的猜测值,并把用户预期的输出值赋给 y,代码设置 如下: >>x = [0;0;0]; >>u = 0; >>y = [3;3]; • 使用索引变量规定模型的输入、输出和状态中哪些可变,哪些不可变,代码设置如下: >>ix = []; >>iu = []; >>iy = [1;2]; • 使用 trim 命令来求出平衡点,代码设置如下: >>[x,u,y,dx] = trim(’exampbalance’,x,u,y,ix,iu,iy) • 得到如下结果: x= 0.4000 0.0000 1.1314 u= 1.2000 y= 1.6000 0.4000 dx = 1.0e-023 * 0 0 0.1031 图 7-69 求解系统平衡点 –293– MATLAB 7.0 从入门到精通 注意:并不是所有求解平衡点的问题都有解。 如果无解,trim 将返回一个离期望状态的偏差最小 的一个解。trim 命令还有其他的调用格式,请读者查阅在线帮助。 7.9 模型的调试 与许多高级编程语言一样,Simulink 6.0 也提供了一个调试器供用户查找和诊断模型中的 错误,它允许用户通过单步运行(逐个方法地运行)仿真和显示模块的即时状态、输入和输 出来查明错误。在 Simulink 6.0 中,用户既可以通过图形化的调试界面进行调试,也可以通 过命令行进行调试。 7.9.1 Simulink 调试器 本小节通过一个示例简单地向读者介绍如何使用调试器。该示例是采取单步运行仿真的 方式,用户可以通过这种运行方式来查看在每一个仿真步中各个模块的运行情况,从而可以 检查模块功能是否与用户的设想一致。 • 打开第 7.3.2 节中如图 7-41 所示的示例。 • 选择 Tools→Simulink Debugger 菜单打开如图 7-70 所示的调试器,表 7-26 对该对话框 工具栏各按钮的功能进行了详细介绍。 表 7-26 调试器工具栏 工具栏按扭 功能 进入当前方法 跳过当前方法 跳出当前方法 在下一个仿真时间步跳转到第一个方法 跳转到下一个模块方法 开始或继续调试 暂停仿真 停止仿真 在运行到下一个模块前跳出 当选中的模块被执行时显示其输入输出 显示选中的模块的当前输入输出 选择动画模式 显示调试器的帮助 关闭调试器 调试器窗口界面中的各项设置的含义如下: “points”页:用于设置断点,即仿真运行到某个模块方法或满足某个条件就停止; “Simulation Loop”页:包含 Method、Breakpoints 和 ID 三列内容。用于显示当前仿真步 正在运行的相关信息; “Outputs”页:用于显示调试结果,包括调试命令提示、当前运行模块的输入、输出和 模块的状态。如果是采用命令行调试,这些结果在 MATLAB 7.0 命令窗口中也会显示。调试 命令提示显示当前仿真时间、仿真名和方法的索引号; –294– 第 7 章 Simulink 仿真环境 “Sorted List”页:用于显示被调试的模块列表,该列表按模块执行的顺序排列; “Status”页:用于显示调试器各种选项设置的值以及其他状态信息。 • 单击 按钮开始调试,在“Simulation Loop”页将显示当前运行的方法的名字,并且 该方法也将显示在模块窗口中,如图 7-71 所示。当调试开始后,MATLAB 的命令窗 口也会进入调试状态,如图 7-72 所示。用户可以在命令窗口采取命令行方式进行调试。 • 单击 按钮进行仿真,如图 7-73 所示。在仿真过程中还可以设置断点(非条件中断或 条件中断);既可以按模块单步执行,也可以按时间步单步执行,还可以显示模型的信 息等设置,用户可以查阅在线帮助,在此不做详细地介绍。 图 7-70 调试器窗口 图 7-71 开始仿真 图 7-72 调试状态下的命令窗口 图 7-73 单步仿真 –295– MATLAB 7.0 从入门到精通 7.9.2 命令行调试 在命令行调试模式下,用户可以在 MATLAB 7.0 命令窗口中通过相应的命令来控制调试 器。由于命令行调试方式需要用户记住许多的命令函数,所以一般用户可以直接通过调试用 户界面的相关菜单和工具栏来操作,这里只向读者简单地介绍对于理解命令行调试很关键的 几个基本概念,在后面地章节将陆续介绍一些常用的调试命令。 调试器接受缩写的调试命令,关于这些命令请读者查阅帮助文件。用户还可以通过在 MATLAB 7.0 命令行键入 Enter 键来重复执行前一命令。许多 Simulink 命令和消息是通过 Method ID 和 Block ID 来引用方法和模块的,Method ID 是按方法被调用的顺序从 0 开始分 配的一个整数;Block ID 是在仿真的编译阶段分配的,形式为 sid:bid,sid 是一个用来表示系 统的一个整数,bid 是模块在系统中的位置。用户可以通过 slist 命令来查看当前运行模型的 每一个模块的 ID。 用户可以通过下面两个命令来启动调试器: sim(’vdp’,[0,10],simset(’debug’,’on’)) sldebug ’vdp’ 其中的 vdp 是模型名。 7.9.3 设置断点 所谓断点就是指仿真运行到此处会停止仿真的地方,当仿真遇到断点停止时,用户可以 使用 continue 命令跳过当前断点继续运行到下一个断点。调试器允许用户定义两种断点,即 无条件断点和有条件断点,所谓无条件断点是指运行到此处就停止,而条件断点是指当仿真 过程中满足用户定义的条件时才停止仿真。 如果用户知道自己的程序中某一点或当某一条件满足时就会出错,那么设置断点将很有 用。下面向读者介绍两种设置断点的方法。 1.设置无条件断点 有 3 种方式设置。 • 通过调试器的工具栏 先在模型窗口中选择要设置断点的模块,然后用鼠标左键单击 按钮。在如图 7-74 所示 的示例模型中的 Scope 模块处设置断点,可用“Remove selected point”按钮删除已设置好的 断点。 • 通过调试器的 Simulation Loop 页 选择该页的 Breakpoints 列中要设置的断点处选择前面的选择框即可。 • 通过在 MATLAB7.0 命令窗口运行相关命令 使用 break 和 bafter 命令可以分别在一个方法的前面和后面设置断点,使用 clear 命令清 除断点。 break 命令的语法,代码如下: break break m:mid // mid 为方法 ID –296– 第 7 章 Simulink 仿真环境 break [mth] [tid:TID] // sid:bid 为模块 ID,gcb 为当前选中的模块,TID 为任务 ID,mth 为方法名,如:Outputs.Major break [mth] [tid:TID] // sid 为系统 ID,gcs 为当前选中的系统 break mdl [mth] [tid:TID] // mdl 为当前选中的模型 bafter 和 clear 的语法与之类似,在此不作详细介绍,请读者查阅帮助。 图 7-74 通过工具栏设置断点 2.设置有条件断点 设置有条件断点可以通过在调试器“Break on conditions”页中设置相应的断点条件来实 现,也可以通过以下命令来设置: tbreak:在仿真时间步处 minor:在引起最小时间步的方法处 nanbreak:当发生上溢或下溢时 xbreak:当仿真到决定时间步长的状态时 zcbreak:当在一个时间步内发生过零时 7.9.4 显示仿真的有关信息 Simulink 调试器提供了许多命令允许用户在模型运行时显示模块的状态、模块的输入/ 输出和其他的信息。在这里主要介绍如何使用调试器工具条中的按钮来执行相应的信息显示, 当然这些按钮也有相应的命令,在此不详细介绍。 按钮 用于显示模块的输入/输出信息。首先在模型窗口选中模块,然后用鼠标左键单击 该按钮,被选中的模块在当前仿真步的输入、输出和状态信息将显示在调试器窗口的 “Outputs”页中,相应的命令有 probe 和 disp。在如图 7-75 所示的示例模型中进行单步调试 (方法按第 7.9.1 节介绍的方法进行)到下面的时间步 [Tm = 1.205705487124599e-005 ] 0:5 Gain.Outputs.Minor ’Msubsystem/Subsystem/Gain’ (sldebug @64): 然后就可以看到图中的信息,如图 7-75 所示。使用相应的 probe 命令来显示模块信息, probe 命令的语法如下: probe:进入或退出 probe 模式,当进入时用于显示选中模块的输入输出,如图 7-75 所示。 probe gcb:显示选中模块的当前输入输出; –297– MATLAB 7.0 从入门到精通 probe s:b:s 为系统的序号,b 为模块编号,如图 7-76 所示。0:4 对应示例 Msubsystem 系统中 Subsystem 子系统的 Add 模块 disp 命令可实现相同的功能,其使用语法与 probe 类似。 图 7-75 通过按钮显示模块信息 按钮 也是用于显示模块的信息,与 按钮不同的是,如果用户为某一模块设置了断点, 那么用户就可以在“Break points”页的 Break/Display points 面板中选中 列的相应模块的选 框,一旦为某一模块设置了该选框,那么仿真在每一次运行到该模块处时就会显示它的相关 信息。对应的可以分别使用 trace 和 untrace 命令来完成同样的功能设置和撤销设置。atrace 命令用于显示代数环的信息,其语法及含义如表 7-27 所示。 states 命令用于在 MATLAB 7.0 命令窗口中显示系统的当前信息,如对图 7-75 中的示例 运行该命令即可得到: (sldebug @64): >> states Continuous States: Idx Value (system:block:element Name ’BlockName’) 0 1.205712755753207e-005 (0:0:0 CSTATE ’Msubsystem/Subsystem/Integrator’) 1. 6.0285347042516e-005 (0:0:1) –298– 图 7-76 通过 probe 命令显示模块信息 第 7 章 Simulink 仿真环境 ishow 命令用于锁定积分信息的显示。当它被使能时,那么仿真时间步每前进一次或遇 到需要改变仿真步长的状态时,调试器都将显示一个相应的消息。 表 7-27 命令 atrace 0 atrace 1 atrace 2 atrace 3 atrace 4 trace 命令 显示的信息 没有信息显示 显示环路变量的解,求解环路所需的迭代次数,以及估计的解的误差 与 atrace 1 相同 除了 atrace2 所显示内容外,还显示求解环路的 Jacobian 矩阵 除了 atrace 3 所显示内容外,还显示环路变量在每次迭代时的瞬时解 7.9.5 显示模型的信息 调试器除了可显示关于仿真的信息外,还可显示有关模型的信息。 slist 命令用于显示系统中各模块的索引,模块的索引其实也是按它们执行的顺序来排列 的,对图 7-75 中的示例运行 slist 命令将看到如下信息: (sldebug @64): >> slist ---- Sorted list for ’Msubsystem’ [8 nonvirtual blocks, directFeed=0] 0:0 ’Msubsystem/Subsystem/Integrator’ (Integrator, tid=0) 0:1 ’Msubsystem/Scope’ (Scope, tid=0) 0:2 ’Msubsystem/Constant’ (Constant, tid=1) 0:3 ’Msubsystem/Sine Wave’ (Sin, tid=0) 0:4 ’Msubsystem/Subsystem/Add’ (Sum, tid=0) 0:5 ’Msubsystem/Subsystem/Gain’ (Gain, tid=0) 0:6 ’Msubsystem/Floating Scope1’ (Scope, tid=1) 0:7 ’Msubsystem/Scope1’ (SignalViewerScope, tid=0) 这与调试器窗口中“Sorted List”页显示的内容一样。 其他用于显示模块信息的命令还有 bshow、systems、zclist、ashow、status 等,它们的使 用方法与前面的命令也非常类似,具体的操作细节和所实现的功能请读者查阅帮助。 7.10 S-函数 S-函数(System-函数)为扩展 Simulink 的功能提供一个强有力的机制,本小节将向读者 解释以下几个问题: • S-函数是什么? • 用户为什么要使用 S-函数? • 用户怎样书写自己的 S-函数? –299– MATLAB 7.0 从入门到精通 7.10.1 什么是 S-函数 S-函数是一种描述动态系统的计算机语言,用户可以用 MATLAB、C、C++、Ada 或 Fortran 语言书写。C、C++ 、Ada 和 Fortran 书写的 S-函数用 mex 命令编译成 MEX 文件,它们就像 MATLAB 中其他的 MEX 文件一样可以动态的连接到 MATLAB。本文只介绍用 MATLAB 语 言书写的 S-函数。 S-函数采用一种特殊的调用语法使得函数可以和 Simulink 方程解法器进行交互,这种交 互与解法器和 Simulink 系统自身提供的模块间的交互十分类似。S-函数的形式十分通用,用 户可以用它描述连续、离散和混合系统。 7.10.2 为何要用 S-函数 通过 S-函数,用户可以实现以下操作: • 用户可以通过它用多种语言来创建新的通用性的 Simulink 模块; • 只需遵循一系列简单的规则,用户即可将自己的算法用 S-函数实现。当用户写好自己 的 S-函数后,可以在 User-Defined Functions 模块库中的 S-function 模块中通过名称来 调用编写好的 S-函数,用户还可以封装它; • 用户可以通过 S-函数将一个系统描述成一个数学方程; • 便于使用图形化仿真; • 可以创建代表硬件驱动的模块。 使用 S-函数的主要优点是用户可以通过它创建通用性模块,可以多次调用,而且可以每 次使用不同的参数值。 7.10.3 S-函数如何工作 要理解 S-函数如何工作,读者应该先理解如何用数学描述一个模块以及 Simulink 如何仿 真一个模型。 1.模块的输入、状态和输出间的数学关系 描述一个 Simulink 模块需要 3 个基本元素,即输入向量(u)、状态向量(x)和输出向 量(y),输出是输入向量和采样时间的函数: y = f0 (t,u, x) x&c = fd (t, x,u) xdk+1 = fu (t, x,u) 输出 微分 更新 x = xc + xd Simulink 在仿真时把上面的这些方程对应为不同的仿真阶段,它们分别是计算模块的输 出、更新模块的离散状态和计算连续状态的微分。在仿真的开始和结束,还包括初始化和结 束任务两个阶段。在每一个阶段,Simulink 都重复地对模型进行调用。 –300– 2.Simulink 运行仿真的过程 第 7 章 Simulink 仿真环境 仿真按照如图 7-77 所示的流程进行,由图可知仿真是分阶段进行的。在初始化阶段, Simulink 将库中的模块并入到用户自建模型中,确定模块端口的数据宽度、数据类型和采样 时间,评估模块参数,决定模块运行的优先级,定位存储地址;然后进入仿真循环,每一次 的仿真循环称为一个仿真步。在每一个仿真步中的不同仿真阶段,Simulink 按优先级运行模 型中的每一个模块。如果在积分时对仿真的时间步长有要求,则此时需要将时间步细化;完 成一个仿真循环(也即一个仿真步)后,就进入下一个循环仿真步,如此循环直至仿真结束。 含有 S-函数模块的模型的仿真过程与此类似。 图 7-77 仿真执行流程图 3.S-函数的回调方法 一个 S-函数是由一系列 S-函数回调的方法组成,在每次仿真循环中的每一个仿真阶段由 Simulink 调用相应的 S-函数回调方法来执行相应的任务。至于 S-函数有哪些回调方法,以及 Simulink 如何确定在某一个仿真阶段调用哪一个回调方法,将在下一小节介绍。 与一般模型的仿真类似,S-函数回调方法用以完成以下任务: • 初始化:在进入第一个仿真循环之前,Simulink 初始化 S-函数。在此阶段,Simulink 主要完成初始化 SimStruct(SimStruct 包含 S-函数信息的数据结构),确定输入输出端 口的数目和大小,确定模块的采样时间,分配内存和 Sizes 数组的工作。 • 计算下一个采样点。如果模型使用变步长解法器,那么就需要在当前仿真步确定下一 个采样点的时刻,即下一个仿真步的大小。 • 计算当前主仿真步的输出。当这次回调完成后,模块的所有输出端口的值对当前仿真时 间步有效,即模块的输出被更新后才能作为其他模块的有效输入去影响那些模块的行为。 • 更新当前主时间步的离散状态。在此仿真阶段,所有的模块都要为当前时间的仿真循 环更新离散状态。 • 积分。只有当模块具有连续状态或者非采样过零点时,Simulink 才会有这一仿真阶段。 对于 S-函数,在该仿真阶段 Simulink 按最小时间步来调用 S-函数的输出和微分 S-函 数方法。如果 S-函数具有非采样过零点,Simulink 按最小时间步来调用 S-函数中的输 出和过零点部分,以便 Simulink 确定过零点的位置。 –301– MATLAB 7.0 从入门到精通 7.10.4 怎样书写 S-函数 下面将使用 MATLAB 语言来书写 S-函数称为 M 文件 S-函数,每一个 M 文件 S-函数包 含一个如下形式的 M 函数: [sys,x0,str,ts]=f(t,x,u,flag,p1,p2,...) 表 7-28 列出了上式中各参数的含义。在这类 S-函数中的 S-函数回调方法是用 M 文件子 函数的形式来实现的。 表 7-28 函数各参数的含义 参数名 f t x u flag p1,p2,… ts sys x0 str 参数含义 S-函数的名称 当前仿真时间 S-函数模块的状态向量 S-函数模块输入 用以标示 S-函数当前所处的仿真阶段,以便执行相应的子函数 S-函数模块的参数 向 Simulink 返回一个包含采样时间和偏置值的两列矩阵。不同的采样时刻设置方法对 应不同的矩阵值。如果希望 S-函数在每一个时间步都运行,就设为[0 0];如果希望 S函数模块与和它相连的模块以相同的速率运行,就设为[-1 0];如果希望可变步长,则 设为[2 0];如果希望从 0.1s 开始每隔 0.25s 运行一次,就设为[0.25 0.1];如果你的 S函数执行多个任务,而每个任务运行的速率不同,可设为多维矩阵,两个任务设为[0.25 0; 1.0 0.1]… 用以向 Simulink 返回仿真结果的变量。根据不同的 flag 值,sys 返回的值也不完全一 样(因为不同的 flag 对应不同的仿真阶段和仿真任务,仿真也就得到不同的结果) 用以向 Simulink 返回初始状态值 保留参数 在模型仿真过程中,Simulink 重复地调用 f,并根据 Simulink 所处的仿真阶段为 flag 参 量传递不同的值,同时为 sys 变量指定不同的角色(不同的角色对应不同的返回值)。flag 用 来标示 f 函数要执行的任务,以便 Simulink 调用相应的子函数(也即 S-函数的回调方法)。 因此,用户在编写 M 文件 S-函数时只需用 MATLAB 语言来为每个 flag 值对应的 S-函数 方法编写代码即可。表 7-29 列出了在各个仿真阶段对应要执行的 S-函数回调方法以及相应的 flag 参数值。 表 7-29 各个仿真阶段对应要执行的 S-函数方法 仿真阶段及方法说明 初始化。定义 S-函数模块的基本特性,包括采样时间、 连续或离散状态的初始条件和 Sizes 数组 计算下一个采样点的绝对时间。该方法只有在用户在 mdlInitializeSizes 说明了一个可变的离散采样时间时可 用 计算输出 更新离散状态 计算微分 结束仿真 S-函数方法 mdlInitializeSizes mdlGetTimeOfNextVarHit mdlOutputs mdlUpdate mdlDerivatives mdlTerminate Flag flag = 0 flag = 4 flag = 3 flag = 2 flag = 1 flag = 9 用户可以通过在 MATLAB 7.0 命令窗口输入“<0 I= -25e-6; //当 down>0 时,输出负电流,大小为-25uA,不同的电荷泵取值不一样 else I= 0; //当 down≤0 时,输出 0 电流 end Embedded MATLAB Function1 模块中的文件如下: function I = chargpump2(up) if up>0 I=25e-6; //当 up>0 时,输出正电流,大小为 25uA,不同的电荷泵取值不一样 else I=0; //当 up≤0 时,输出 0 电流 end 滤波器的功能由 Ports&Subsystem 子库中的使能子系统来实现,两个使能子系统模块的 内部结构完全一样,如图 7-83 所示。 图 7-83 滤波器子系统 其中 Transfer Fcn 模块的参数设置如图 7-84 所示。Enable 模块的“States when enabling” 参数设为 reset,若设为 held,那么滤波器(Transfer Fcn 模块)不会将输入电流的跳变当成一 个阶跃信号来处理。这样每一次使能信号有效后,Transfer Fcn 模块的状态就会重置成初始状 态,体现在输出上就是在每一次 Enable 信号有效时输出都是从 0 开始变化。这也是在系统模 型中用两个滤波器来分别处理输入的正阶跃和负阶跃电流的原因。系统的最终输出是两个滤 波器输出的和,因为 up 和 down 不会同时为高电平,所以这样做可以保证每一时刻的输出都 能反映前面所有时间的输入对系统状态的积累效果。如果要考虑有 up 和 down 同时为高电平 的情况,那么可以在 up 输入和 down 输入与 Embedded MATLAB Function 模块的输入端口之 间加一模块,使得在此种情况下进入 Embedded MATLAB Function 模块 down 端口和 Embedded MATLAB Function1 模块 up 端口的信号值都为 0,即没有电流输入到滤波器,这与 实际相吻合。 Enable 模块还有另外一个作用是保证滤波器只对使能信号上升边沿的阶跃信号进行处 理,即只接收 Idown 从 0→-25 和 Iup 从 0→25 的阶跃信号,而不接收 Idown 从-25→0 和 Iup 从 25→0 的阶跃信号。读者还可以试着将子系统中的 Enable 模块换成 Trigger 模块,观察会 出现什么问题。 如果读者对上面这些有关信号处理的知识不了解的话,那么你也可以不必知道为什么要 这样设置,而只需学习模型创建的过程即可。 –308– 第 7 章 Simulink 仿真环境 图 7-84 Transfer Fcn 模块的参数设置 • 封装子系统 对由使能子系统构造的滤波器模块进行封装(方法参见第 7.3.2 节),其中封装的设置情 况如图 7-85 所示。用鼠标左键双击封装好的使能子系统图标将会弹出如图 7-86 所示参数设 置对话框,用户可在此对话框中设置子系统的参数。 • 设置参数 在第二步中已对各主要模块的参数设置都进行了说明,下面主要设置与仿真有关的设 置。主要需要设置的是仿真开始和结束的时间、解法器的类型和解法器、最大和最小仿真步 长以及相对和绝对的仿真误差容忍度等,具体设置如图 7-87 所示。读者可以对其中的设置进 行修改,然后进行仿真,观察输出因设置的不同而相应发生了哪些变化。 图 7-85 使能子系统的封装参数设置 图 7-86 封装使能子系统的参数设置对话框 –309– MATLAB 7.0 从入门到精通 图 7-87 仿真参数设置 • 保存模型 为了仿真方便,读者可以将模型中的 up 和 down 输入模块用一信号源模块(如 Pulse Generator、Sine Wave 等)代替,这样就不用通过 MATLAB 命令来为模型添加输入信号。如 果仿真结果不够理想,那么可以调整模块参数和仿真参数的设置,直至仿真结果吻合与建模 用户预想的情况相符。仿真结果如图 7-88 和、图 7-89 和图 7-90 所示,相关模块的参数设置 如下: 滤波器子系统模块中 R=31.8e3,C1=62.2e-12,C2=6e-12;输入信号为脉冲信号,周期为 2e-6、幅度为 1、占空比 50% ,up 和 down 相位相差 180°;仿真参数的设置如图 7-87 所示。 –310– 图 7-88 Iup 和 Idown 仿真结果 第 7 章 Simulink 仿真环境 图 7-89 Vc_Down、Vc 和 Vc_Up 的仿真结果 由图 7-89 可以看出滤波器只对 Iup 的上升跳变和 Idown 的下降跳变有效,而且每当滤波 器的使能信号有效时,它的输出都是从 0 开始变化。 若将模型中上面的一个滤波器子系统中 Enable 模块的“States when enabling”参数设为 held,那么仿真结果将如图 7-90 所示。对比图 7-89 和图 7-90,可以证明第二步中所述分析是 正确的。改为 held 后,只有开始的一个电流跳变被当作阶跃信号来处理,其余的均当作常量 来处理的,因为对应处的 Vc_Down 是线性变化的。 图 7-90 改变 Enable 模块的参数后的仿真结果 • 调试 有时用户对所建模型的输出结果并没有一个预想的值,因此只是简单的通过观察结果并 –311– MATLAB 7.0 从入门到精通 不能证明所建模型的正确性,这时就需要进行调试(方法参见第 7.9 节),以观察系统的每一 个模块在每一个时间步的运行情况。对于上面的模型,可能会因为读者的操作系统和 MATLAB 7.0 的安装情况不同而有所不同,在此不作详细的解释。 本小节通过创建一个较为复杂的模型向读者展示了在实际的建模过程中,用户会经常碰 到的某些问题。示例中遇到的最大问题是当 Simulink 库中没有完全满足用户要求的现成模块 时,用户该如何去利用已有模块来构造需要的模块,以及如何验证模型的正确性。当然只通 过一个示例不足以展示所有建模的细节问题,但是通过对本章内容的全面学习,相信读者将 会知道如何去解决实际操作中碰到的各种具体问题。 –312– 第 8 章 MATLAB 7.0 的符号计算功能 符号数学工具箱中的工具是建立在功能强大的 Maple 软件的基础上。它最初是由加拿大 的滑铁卢(Waterloo)大学开发出来的。如果要求 MATLAB 7.0 进行符号运算,那么首先由 Maple 计算并将结果返回到 MATLAB 7.0 命令窗口。因此可以说 MATLAB 7.0 中的符号运算 是它处理数字运算的自然扩展。 进行符号计算,第一,计算以推理解析的方式进行,因此不受计算误差累积所带来的 困扰;第二,符号计算可以给出完全正确的封闭解或任意精度的数值解(当封闭解不存在 时);第三,符号计算指令的调用比较简单,与经典教科书公式相近;第四,计算所需要的 时间较长。 在 MATLAB 7.0 中,符号计算虽以数值计算的补充身份出现,但设计符号计算的相关指 令、符号计算结果的图形化显示、符号计算程序的编写以及在线帮助系统都是十分完整和便 捷的。 MATLAB 7.0 的升级和符号计算内核 Maple 的升级,决定了符号计算工具包的升级。但 从用户使用的角度来看,这些升级所带来的变化相当细微。本章在相关的部分也给出了声明。 8.1 符号运算入门 科学与工程技术中的数值运算固然重要,但自然科学理论分析中各种各样的公式、关系 式及其推导就是符号运算要解决的问题。它与数值运算一样,都是科学计算研究的重要内容。 MATLAB 7.0 数值运算的对象是数值,而 MATLAB 7.0 符号运算的对象则是非数值的符号对 象。所谓符号对象就是指代表非数值的符号字符串。下面列举一些实例来具体说明 MATLAB 7.0 的符号运算功能。 8.1.1 求解一元二次方程 x2 + 2x + 2 = 0 的根 这是一个求解一元二次方程数值根的数字计算问题。根据求根公式,很容易得到方程的 两个数值根是 x1,2 = −1 ± j 。 而对于一元二次方程一般式 ax 2 + bx + c = 0 来说,要求它的根,就必须根据一元二次 方程求根公式,得到方程的根为 x1,2 = − b ± b2 − 4ac 。这就是一个求一元二次方程根的 2a 符号运算问题。 MATLAB 7.0 里的符号运算功能正是用来解决这类问题的。只要运行以下的函数命令: solve(’a*x^2+b*x+c=0’) 即可求得一元二次方程的两个根: ans = 1/2/a*(-b+(b^2-4*a*c)^(1/2)) 1/2/a*(-b-(b^2-4*a*c)^(1/2)) 求解一元二次方程数值根的计算问题也可以运行相同的 MATLAB 7.0 函数命令。例如求 方程 x2 + 2x + 2 = 0 的根时,可运行如下的语句: solve(’x^2+2*x+2=0’) 即可得到方程的根: ans = -1+i -1-i 8.1.2 求导数 d (cos2 x) dx 根据复合函数求导公式,有 d (cos2 x) = 2 cos x(− sin x) = −2 sin x cos x 。可以运行以 dx 下 MATLAB 7.0 语句求复合函数的导数: x=sym(’x’); diff(cos(x)^2) 即可求得导数: ans = -2*cos(x)*sin(x) b 8.1.3 计算定积分 ∫ x2dx a ∫ ∫ 计 算 定 积 分 b x 2dx 即 做 运 算 b x 2dx = 1 x3 b = 1 (b3 − a3 ) 。 可 以 运 行 以 下 两 条 a a 3 a3 MATLAB 7.0 语句: syms x a b; int(x^2,a,b) 即得到定积分的结果: ans = 1/3*b^3-1/3*a^3 –314– 8.1.4 求解一阶微分方程 dy = ay dt 第 8 章 MATLAB 7.0 符号计算功能 ∫ ∫ 即作运算 dy = y adt , ln y = at + c , y(t) = eat+c = c1eat 。 可以运行以下 MATLAB 7.0 语句: syms a y; dsolve(’Dy=a*y’) 即求得一阶微分方程的解: ans = C1*exp(a*t) 从以上几个例子可以看出 MATLAB 7.0 符号运算的对象全是文字符号,算得的结果也是 文字符号,同时符号运算基本上覆盖了初等数学以及高等数学中的绝大多数内容,而且这些 运算都能够用 MATLAB 7.0 函数命令行来实现。 8.2 符号对象的创建和使用 符号数学工具箱中定义了一种新的数据类型,叫 sym 类。sym 类的实例就是符号对象, 符号对象是一种数据结构,是用来存储代表符号的字符串的。在符号数学工具箱中,用符号 对象来表示符号变量、符号表达式和符号矩阵。 本节将介绍如何借助 MATLAB 7.0 的符号数学工具箱来创建和使用符号变量、符号表达 式和符号矩阵,同时介绍 MATLAB 7.0 的默认符号变量及其设置方法。 8.2.1 创建符号对象和表达式 在一个 MATLAB 7.0 程序中,作为符号对象的符号常量、符号变量、符号函数以及符号 表达式,可以使用函数命令 sym()、syms()加以规定和创建,利用 class()可以测试建立的操作 对象为何种操作对象类型以及是否为符号对象类型。 (1)函数命令 sym() 函数命令 sym()的调用格式有如下两种: S=sym(A,flag) S=sym(’A’,flag) 命令公式是由 A 来建立一个符号对象 S,其类型为 sym 类型。如果 A(不带单引号)是 一个数字、数值矩阵或数值表达式,则输出是将数值对象转换成的符号对象。如果 A(带单 引号)是一个字符串,输出则是将字符串转换成的符号对象。 其中 flag 为转换的符号对象应该符合的格式。如果被转换的对象为数值对象,flag 可以 有如下选择: • ’d’—最接近的十进制浮点精确表示; –315– • ’e’—带(数值计算时)估计误差的有理表示; • ’f’—十六进制浮点表示; • ’r’—为缺省设置时,最接近有理表示的形式。 对于被转换对象为字符串时,flag 有如下几种选项: • ’positive’—限定 A 为正的实型符号变量; • ’real’—限定 A 为实型符号变量; • ’unreal’—限定 A 为非实型符号变量。 (2)函数命令 syms() 函数命令 syms()的调用格式为: syms s1 s2 s3 flag; 该命令可以建立 3 个或多个符号对象,如 s1、s2、s3。同样,flag 为对转换格式的限定, 具体选项同上。 (3)函数命令 class() 函数命令 class()的调用格式如下: str=class(object) class()命令将返回对象的数据类型。 以下就分别来介绍在不同情况下这些函数的具体使用方法。 1.符号常量 符号常量是一种符号对象。数值常量如果作为函数命令 sym()的输入参量,这就建立了 一个符号对象——符号常量,虽然看上去它是一个数值量,但它已是一个符号对象了。创建 这个符号对象可以用 class()函数来检测其数据类型。 【例 8.1】对数值 3/4 创建符号对象并检测数据的类型。 【解】可以用以下 MATLAB 7.0 语句来创建符号对象并检测数据的类型: a = 3/4; b = ’3/4’; c = sym(3/4); d = sym(’3/4’); classa = class(a) classb = class(b) classc = class(c) classd = class(d) 语句的执行结果为: classa = double classb = char classc = sym classd = sym 即 a 实双精度浮点数值类型;b 实字符类型;c 和 d 都是符号对象类型。 –316– 2.符号变量 第 8 章 MATLAB 7.0 符号计算功能 变量是程序语言的基本元素之一。MATLAB 7.0 符号运算中,符号变量是内容可变的符 号对象。符号变量通常是指一个或几个特定的字符,不是指符号表达式,甚至可以将一个符 号表达式赋值给一个符号变量。符号变量有时也称为自由变量。符号变量与 MATLAB 7.0 数 值变量名称的命名规则相同: • 变量名可以由英文字母、数字和下划线组成; • 变量名应以英语字母开头; • 组成变量名的字母长度不大于 31 个; • MATLAB 7.0 中区分大小写英语字母。 在 MATLAB 7.0 中可以用函数命令 sym()和 syms()来建立符号变量。 【例 8.2】用函数命令 sym()和 syms()建立符号变量 a、b、c。 【解】 (1)用函数命令 sym()来创建符号对象并检测数据的类型 用 sym()命令来创建符号对象并检测数据类型的代码如下: a = sym(’a’); b = sym(’b’); c = sym(’c’); classa = class(a) classb = class(b) classc = class(c) 语句执行的结果如下: classa = sym classb = sym classc = sym 可以看出得到的 3 个变量均为符号对象。 (2)用函数命令 syms()来创建符号对象并检测数据的类型 用 syms()命令来创建符号对象并检测数据类型的代码如下: syms a b c; classa = class(a) classb = class(b) classc = class(c) 语句执行的结果如下: classa = sym classb = sym classc = sym 从上面的两种实现方法可以看出,对于需要同时定义多个符号变量的情况,使用 sym() 命令分别定义显得过于繁琐,而可以采用 syms()命令进行代替。MATLAB 7.0 提倡用 syms() 命令,因为它需要很少的书写,比较符合 MATLAB 7.0 符号运算简洁的特点。 –317– 3.符号表达式、符号函数与符号方程 表达式也是程序设计语言的基本元素之一。在 MATLAB 7.0 符号运算中,符号表达式是 由符号常量、符号变量、符号函数运算符以及专用函数连接起来的符号对象。符号表达式有 两类:符号函数与符号方程。符号函数不带等号,而符号方程是带等号的。 在 MATLAB 7.0 中,同样采用命令 sym()或 syms()来建立符号表达式。下面看一个建立 符号函数的例子。 【例 8.3】用函数命令 sym()与 syms()建立符号函数 f1、f2、f3。 【解】用函数命令 sym()与 syms()来创建符号函数: syms x y z; f1 = x*y/z; f2 = x^2+y^2+z^2; f3 = f1/f2; 【例 8.4】用函数命令 sym()来创建符号方程 e1、e2、e3。 【解】具体的实现命令如下: e1 = sym(’a*x^2+b*x+c’) e2 = sym(’sin(x)^2+2*cos(x)=1’) e3 = sym(’Dy-y=x’) 4.符号矩阵 元素是符号对象(非数值符号的字符符号即符号变量与符号形式的数即符号常量)的矩 阵叫做符号矩阵。符号矩阵既可以构成符号矩阵函数,也可以构成符号矩阵方程,它们都是 符号表达式。 【例 8.5】用函数命令 sym()建立符号矩阵 m1、m2。 【解】具体的实现命令如下: m1 = sym(’[ab bc cd;de ef fg;h I j]’); m2 = sym(’[1 12;23 34]’); 从上面的例子可以看出,创建符号矩阵的方法可以概括为两种:sym()指令直接输入法, 如 m1 的实现;数值矩阵转换法,如 m2。这里需要重点指出的是第二种方法,数值矩阵转换 法,把这种方法同 MATLAB 7.0 种许多数值矩阵的生成函数配合起来,可以产生出许多特殊 的符号矩阵。 8.2.2 符号对象的基本运算 由于新版 MATLAB 7.0 采用了重载技术,使得构称符号计算表达式的运算符和基本函数, 无论在形状、名称上,还是在使用方法上,都与数值计算中的运算符和基本函数几乎完全相 同。这无疑给用户带来了极大的方便。 下面就符号计算种的基本算符和函数作一些简要的归纳。 –318– 1.基本运算符 第 8 章 MATLAB 7.0 符号计算功能 • 运算符“+”、“-”、“*”、“\”、“/”、“^”分别实现矩阵的加、减、乘、左除、右除 和求幂运算。 • 运算符“.*”、“./”、“.\”、“.^”分别实现“元素对元素”的数组乘、左除、右 除和求幂运算。 • 运算符“’”、“.’”分别实现矩阵的共轭转置和非共轭转置。 2.关系运算符 在符号对象的比较中,没有“大于”、“大于等于”、“小于”和“小于等于”的概念,而 只有是否“等于”的概念。 运算符“==”和“~=”分别对运算符两边的对象进行“相等”、“不等”的比较。当 事实为“真”时,返回结果 1;否则,返回结果 0。 3.三角函数、双曲函数以及它们的反函数 除 atan2 仅能用于数值计算外,其余的三角函数(如 sin)、双曲函数(如 cosh)及它们 的反函数(如 asin、acosh),无论在数值计算还是在符号计算中,它们的使用方法都相同。 4.指数、对数函数 在数值、符号计算中,函数 sqrt、exp 和 expm 的使用方法完全相同。至于对数函数,在 MATLAB 6.X 版本中,符号计算中只有自然对数 log,而没有数值计算中的 log2、log10。而 在 MATLAB 7.X 版本中,增加了 log2 和 log10 函数,其用法同数值运算中完全相同。 5.复数函数 复数函数涉及复数的共轭(conj)、实部(real)、虚部(imag)和模(abs)的函数,在 符号、数值计算中的使用方法相同。但需要注意的是,在符号运算中,MATLAB 7.0 没有提 供求相角的指令。 6.矩阵代数指令 在符号运算中,MATLAB 7.0 提供的常用矩阵代数指令有 diag、triu、tril、inv、det、rank、 rref、null、colspace、poly、expm、eig 和 svd。它们的用法几乎与数值计算中的情况完全一 样,只有 svd 稍微不同,后面将有详细的介绍。 8.3 任意精度数学计算 符号计算的一个非常显著的特点是由于计算过程中不会出现舍入误差,从而可以得到任 意精度的数值解。如果希望计算结果精确,那么就可以牺牲计算时间和存储空间,用符号计 算来获得足够高的计算精度。 在符号运算工具箱中有 3 种不同类型的算术运算: –319– • 数值类型:MATLAB 7.0 的浮点算术运算; • 有理数类型:Maple 的精确符号计算; • VPA 类型:Maple 的任意精度算术运算。 这 3 种运算各有利弊,在计算时应根据计算精度、时间、存储控件的要求进行合理的选 择。首先来看一下浮点运算和有理数运算的特点。下面是两种运算的例子: format long %指定输出格式 1/2+1/3 %一般的浮点运算 sym(1/2+1/3) %有理数运算(符号运算) 由以上语句得到结果代码如下: ans = 0.83333333333333 ans = 5/6 浮点运算是最快的运算,需要的计算机内存最小,但是结果不精确。MATLAB 7.0 双精 度数输出的数字位数由 format 命令控制,但它内部采用的是由计算机硬件提供的八位浮点的 表示方法。而且,在上面的浮点运算中,存在三种舍入误差,第一是来自 1/3 的除法舍入误 差;第二是来自将 1/2 加到 1/3 的结果上产生的舍入误差;第三是来自将二进制结果转化为 十进制输出时的舍入误差。 而符号运算中的有理数算术运算,它所需要的时间和内存开销都是最大的。只要有足够 的内存和足够长的计算时间,总能产生精确的结果。 一般符号计算的结果都是字符串,特别是一些符号计算结果从形式上看来是数值,但从 变量类型上说,它们仍然是字符串,例如上面例子中的“5/6”实际上就是字符串,而非数值。 要从精确解中获得任意精度的解,并改变默认精度,把任意精度符号解变成“真正的”数值 解,就需要用到 MATLAB 7.0 提供的如下几个函数。 1.digits(d) 调用该函数后的近似解的精度变成 d 位有效数字。d 的默认值是 32 位。 另外,调用 digits 命令(没参数)可以得到当前采用的数值计算的精度。 2.vpa(A,d) 求符号解 A 的近似解,该近似解的有效位数由参数 d 来指定。如果不指定 d,则按照一 个 digits(d)指令设置的有效位数输出。 3.double(A) 把符号矩阵或任意精度表示的矩阵 A 转换成为双精度矩阵。 对于 vpa 函数的输入既可以是符号对象,也可以是数值对象,而其输出一定为符号对象。 【例 8.6】指令使用演示。 下面的命令首先创建一个符号矩阵,然后分别将它转换成任意精度矩阵和双精度矩阵。 A=[1.100 2.300 3.500;4.900 5.400 6;9.100 7.890 4.230]; –320– 第 8 章 MATLAB 7.0 符号计算功能 S=sym(A) 生成符号矩阵如下: S= [ 11/10, 23/10, 7/2] [ 49/10, 27/5, 6] [ 91/10, 789/100, 423/100] 转换成有效位为 4 的任意精度矩阵如下: digits(4); vpa(S) ans = [ 1.100, 2.300, 3.500] [ 4.900, 5.400, 6.] [ 9.100, 7.890, 4.230] 转换成双精度型的矩阵如下: double(S) ans = 1.10000000000000 2.30000000000000 3.50000000000000 4.90000000000000 5.40000000000000 6.00000000000000 9.10000000000000 7.89000000000000 4.23000000000000 8.4 符号表达式的化简和替换 从前面几节可以发现,符号计算所得的结果往往比较繁琐,非常不直观。为此,MATLAB 7.0 专门提供了对符号计算结果进行化简和替换的函数,诸如因式分解、同类项合并、符号表 达式的展开、符号表达式的化简和通分等,它们都是表达式的恒等变换。 8.4.1 符号表达式的化简 MATLAB 7.0 符号工具箱中提供了函数 collect、expand、horner、factor、simplify 和 simple 来实现符号表达式的化简。下面将分别介绍这些函数。 1.函数 collect() 函数实现功能为将符号表达式中同类项合并,具体调用格式有两种: • R=collect(S):将表达式 S 中的相同次幂的项合并。其中 S 可以是一个表达式,也可以 是一个符号矩阵。 • R=collect(S,v):将表达式 S 中 v 的相同次幂的项进行合并。如果 v 没有指定,则缺省 地将含有 x 的相同次幂的项进行合并。 下面是 collect 函数调用的一个例子: 【例 8.7】 –321– syms x t f = (x-1)*(x-2)*(x-3) collect(f) 结果为: ans = -6+x^3-6*x^2+11*x %定义基本变量 %定义符号表达式 %合并 f 中 x 的同类项 2.函数 expand() 函数实现功能为将表达式进行展开。它的调用格式为: • R = expand(S):它将表达式 S 中的各项进行展开,如果 S 包含函数,则利用恒等变形 将它写成相应的和的形式。该函数多用于多项式,有时也用于三角函数、指数函数和 对数函数。 下面是函数 expand 应用的两个例子: 【例 8.8】多项式的展开示例: syms x y; %定义基本变量 f = (x+y)^3; %创建符号表达式 f1 = expand(f) %展开多项式 f 得到结果代码如下: f1 = x^3+3*x^2*y+3*x*y^2+y^3 【例 8.9】三角函数的展开示例: h = cos(x-y) %创建符号表达式 expand(h) %展开三角函数 得到结果代码如下: ans = cos(x)*cos(y)+sin(x)*sin(y) 3.函数 horner() 函数实现将符号表达式转换成嵌套形式。它的调用格式为: • R = horner(S):其中 S 是符号多项式矩阵,函数 horner 将其中每个多项式转换成它们 的嵌套形式。 下面是函数 horner 应用的一个例子。 【例 8.10】 示例代码设置如下: syms x y %定义基本变量 f = x^3-6*x^2+11*x-6 %定义符号多项式 horner(f) %将 f 转换成嵌套形式 得到结果代码如下: ans = -6+(11+(-6+x)*x)*x –322– 4.函数 factor() 第 8 章 MATLAB 7.0 符号计算功能 函数实现将符号多项式进行因式分解。它的调用格式为: • factor(X):如果 X 是一个多项式或多项式矩阵,系数是有理数,那么该函数将把 X 表 示成系数为有理数的低阶多项式相乘的形式;如果 X 不能分解成有理多项式乘积的形 式,则返回 X 本身。 下面是函数 factor 应用的一些例子: 【例 8.11】 示例代码设置如下: syms x y a b factor(x^3-y^3) 得到结果代码如下: ans = (x-y)*(x^2+x*y+y^2) 以下的指令将对如 x^(n+1)的多项式进行因式分解: n = 1:9 x = x(ones(size(n))); p = x.^n+1; [p;factor(p)].’ 输出的矩阵中,每行的第一个元素为分解前的多项式,第二个元素为分解后的多项式。 当 n 为偶数时,x^n+1 无法分解成有理多项式,所以返回它们自身。具体代码如下: ans = [ x+1, x+1] [ x^2+1, x^2+1] [ x^3+1, (x+1)*(x^2-x+1)] [ x^4+1, x^4+1] [ x^5+1, (x+1)*(x^4-x^3+x^2-x+1)] [ x^6+1, (x^2+1)*(x^4-x^2+1)] [ x^7+1, (x+1)*(1-x+x^2-x^3+x^4-x^5+x^6)] [ x^8+1, x^8+1] [ x^9+1, (x+1)*(x^2-x+1)*(x^6-x^3+1)] 此外,函数 factor 还可以类似算术中质因数分解那样对正整数进行最简分解。当输入中 的某个元素超过了 16 位,则必须先将该矩阵用函数 sym 定义成符号矩阵才能进行分解。这 时,factor 是对符号整数进行分解。事实上,具有对整数进行分解功能的是在特殊功能函数 工具箱中的 factor,它与这里讨论的符号工具箱中的 factor 同名。 下面的程序将对符号整数进行与上面类似的形式进行分解: 【例 8.12】 示例代码设置如下: one = ’1’; for n = 1:11 –323– N(n,:) = sym(one(1,ones(1,n))); end [N factor(N)] 得到的结果代码如下: ans = [ 1, 1] [ 11, (11)] [ 111, (3)*(37)] [ 1111, (11)*(101)] [ 11111, (41)*(271)] [ 111111, (3)*(7)*(11)*(13)*(37)] [ 1111111, (239)*(4649)] [ 11111111, (11)*(73)*(101)*(137)] [ 111111111, (3)^2*(37)*(333667)] [ 1111111111, (11)*(41)*(271)*(9091)] [ 11111111111, (513239)*(21649)] 5.函数 simplify() 根据一定的规则对表达式进行简化,它的调用格式为: • R= simplify(S):该函数是一个强有力的具有普遍意义的工具。它应用于包含和式、方 根、分数的乘方、指数函数、对数函数、三角函数、Bessel 函数以及超越函数等的表 达式,并利用 Maple 化简规则对表达式进行简化。其中 S 可以是符号表达式矩阵。 下面是 simplify 函数应用的一个例子。 【例 8.13】 S =sym(’[(x^2+5*x+6)/(x+2);sqrt(16)]’); simplify(S); 得到的结果代码如下: ans = x+3 4 6.函数 simple() 寻找一个符号表达式的最简形式。它的调用格式有: • r = simple(S):用几种不同的算术简化规则对符号表达式进行简化,返回使表达式 S 变 得简短的形式。如果 S 使符号表达式矩阵,则返回使整个矩阵变成最短的形式,而不 一定使每一项都最短;如果不给定输出参数 r,该函数将显示所有使表达式 S 变短的 简化形式,并返回其中最短的一个。 • [r,how] = simple(S):不显示简化的中间结果,只显示寻找到的最短形式以及找到该形 式所用的简化方法。返回值中,r 是符号表达式,how 是一个描述简化方法的字符串。 –324– 第 8 章 MATLAB 7.0 符号计算功能 函数 simple 的目标是使表达式用最少的字符来表示。虽然并非表达式中的字符越少,表 达式就越简洁,但采用这个标准往往能够得到满意的结果。为了达到这个最少字符的标准, simple 函数综合使用了以下几个函数进行不同方式的化简: • 用函数 simplify 对表达式进行化简; • 用函数 radsimp 对包含根式的表达式进行化简; • 用函数 combine 把表达式中以求和形式、乘积形式、幂形式出现的各项进行合并; • 用函数 collect 合并同类项; • 用函数 factor 实现因式分解; • 用函数 convert 把一种形式转换成另一种形式。 函数 simple 比较这些函数的结果,最终把最少字符作为标准。下面是函数 simple 应用的 一些例子,读者可以从中体会到该函数在进行化简时的强大功能。 【例 8.14】 示例代码设置如下: syms x simple(cos(x)^2+sin(x)^2) 输出的结果代码如下: simplify: 1 radsimp: cos(x)^2+sin(x)^2 combine(trig): 1 factor: cos(x)^2+sin(x)^2 expand: cos(x)^2+sin(x)^2 combine: 1 convert(exp): (1/2*exp(i*x)+1/2/exp(i*x))^2-1/4*(exp(i*x)-1/exp(i*x))^2 convert(sincos): cos(x)^2+sin(x)^2 convert(tan): (1-tan(1/2*x)^2)^2/(1+tan(1/2*x)^2)^2+4*tan(1/2*x)^2/(1+tan(1/2*x)^2)^2 collect(x): cos(x)^2+sin(x)^2 mwcos2sin: 1 ans = 1 由于函数 simple 采用多种方法进行化简,所以它往往能够改善函数 simplify 给出的结果, 特别值得一提的是,simple 函数对于含有三角函数的表达式很有效。simple 调用的很多函数 都能把三角多项式进行化简。下面是用 simple 化简三角多项式的示例代码: simple(cos(3*acos(x))) 得到的结果代码如下: ans = 4*x^3-3*x 8.4.2 符号表达式的替换 在 MATLAB 7.0 中,可以通过符号替换来使表达式的输出形式简化,从而可以得到比 较简单的表达式。符号运算工具箱中提供了两个函数 subexpr 和 subs,用于实现符号对象的 –325– 替换。 1.函数 subexpr 函数 subexpr 将表达式中重复出现的字符串用变量代替,它的调用格式如下: • [Y,SIGMA] = subexpr(S,SIGMA):指定用变量 SIGMA 的值(必须为符号对象)来代替 符号表达式(可以是矩阵)中重复出现的字符串。替换后的结果由 Y 返回,被替换的 字符串由 SIGMA 返回; • [Y,SIGMA] = subexpr(S,’SIGMA’):这种形式和上一种形式的不同在于第二个输入参数 是字符或字符串,它用来替换符号表达式中重复出现的字符串。其他参数同上面的形 式相同。 下面是函数 subexpr 的一个例子: 【例 8.15】 syms a x; s = solve(’x^3+a*x+1’) 得到的结果代码如下: s= 1/6*(-108+12*(12*a^3+81)^(1/2))^(1/3)-2*a/(-108+12*(12*a^3+81)^(1/2))^(1/3) -1/12*(-108+12*(12*a^3+81)^(1/2))^(1/3)+a/(-108+12*(12*a^3+81)^(1/2))^(1/3)+1/2*i*3^(1/2)*(1/6*(-108+12*(1 2*a^3+81)^(1/2))^(1/3)+2*a/(-108+12*(12*a^3+81)^(1/2))^(1/3)) -1/12*(-108+12*(12*a^3+81)^(1/2))^(1/3)+a/(-108+12*(12*a^3+81)^(1/2))^(1/3)-1/2*i*3^(1/2)*(1/6*(-108+12*(1 2*a^3+81)^(1/2))^(1/3)+2*a/(-108+12*(12*a^3+81)^(1/2))^(1/3)) 所得到的结果非常繁琐,虽然用函数 simple 不能化简它,但是 subexpr 函数则可以更为 简洁,代码如下所示: r = subexpr(s) 得到的结果代码如下: sigma = -108+12*(12*a^3+81)^(1/2) r= 1/6*sigma^(1/3)-2*a/sigma^(1/3) -1/12*sigma^(1/3)+a/sigma^(1/3)+1/2*i*3^(1/2)*(1/6*sigma^(1/3)+2*a/sigma^(1/3)) -1/12*sigma^(1/3)+a/sigma^(1/3)-1/2*i*3^(1/2)*(1/6*sigma^(1/3)+2*a/sigma^(1/3)) 2.函数 subs 函数 subs 可以用指定符号替换符号表达式中的某一特定符号。它的调用格式有: • R = subs(S):用工作空间中的变量值替代符号表达式 S 中的所有符号变量。如果没有 指定某符号变量的值,则返回值中该符号变量不被替换; • R = subs(S,New):用新符号变量 New 替代原来符号表达式 S 中的默认变量。确定默认 变量的规则与函数 findsym 的规则相同; –326– 第 8 章 MATLAB 7.0 符号计算功能 • R = subs(S,Old,New):用新符号变量 New 替代原来符号表达式 S 中的变量 Old。当 New 是数值形式的符号时,实际上用数值代替原来的符号来计算表达式的值,只是所得结 果仍然是字符串形式。 下面是 subs 应用的一些例子。 【例 8.16】 示例代码如下: subs(exp(a*t),’a’,-magic(2)) 得到的结果代码如下: %用矩阵来替换符号变量 ans = [ exp(-t), exp(-3*t)] [ exp(-4*t), exp(-2*t)] 除了本节介绍的一些简化和替换函数之外,符号数学工具箱还提供了一个将符号表达式 显示起来比较美观的函数 pretty。它在缺省情况下以每一行 79 个字符宽的格式表示符号表达 式,如果表达式中有多次出现的字符串,它会用%n(n 是整数)来代替它们。这是从 Maple 中继承而来的。下面举一个关于 pretty 的例子。 【例 8.17】 示例代码如下: syms a x s = solve(’x^3+a*x+1’) %求解一个方程 pretty(s) 得到的结果代码如下: %使用 pretty 函数来显示结果 [ 1/3 a ] [ 1/6 %1 - 2 ----- ] [ 1/3 ] [ %1 ] [ ] [ 1/3 a 1/2 / 1/3 a \] [- 1/12 %1 + ----- + 1/2 I 3 |1/6 %1 + 2 -----|] [ 1/3 | 1/3|] [ %1 \ %1 /] [ ] [ 1/3 a 1/2 / 1/3 a \] [- 1/12 %1 + ----- - 1/2 I 3 |1/6 %1 + 2 -----|] [ 1/3 | 1/3|] [ %1 \ %1 /] 3 1/2 %1 := -108 + 12 (12 a + 81) –327– 8.5 符号矩阵的计算 在进行符号运算时,很多方面在形式上同数值计算都是相同的,不必再去重新学习一套 关于符号运算的新规则,这给 MATLAB 7.0 用户带来了极大的方便。这里要介绍的符号对象 的矩阵运算在形式上与数值计算中的运算十分相似,读者很容易掌握。 8.5.1 基本代数运算 在 MATLAB 7.0 中,符号对象的代数运算和双精度运算从形式上看是一样的。由于 MATLAB 7.0 中采用了符号的重载,用于双精度数运算的运算符同样可以用于符号对象。 符号对象的加减法运算必须满足下列原则,如果两个对象都是符号矩阵,那么它们必须 大小相等。当然,符号矩阵也可以和符号标量进行加减运算,运算按照数组运算法则进行。 【例 8.18】符号矩阵的加减运算,示例代码设置如下: syms a b c d %定义基本的符号变量 A = sym(’[a b;c d]’); %定义符号矩阵 B = sym(’[2*a 3*b;c+a d+8]’); %定义符号矩阵 A+B %计算符号矩阵的加法 得到的结果代码如下: ans = [ 3*a, 4*b] [2*c+a, 2*d+8] 关于符号矩阵的乘法(包括幂)运算必须要求参与运算的矩阵符合矩阵相乘的规则,例 如第一个矩阵的列数等于第二个矩阵的行数等。对于这些基本的操作,限于篇幅,这里就不 再赘述了,读者可以按照数值计算的部分进行练习。 8.5.2 线性代数运算 符号对象的线性代数运算和双精度数的线性代数运算一样,读者可以参阅本书第 2 章的 有关内容来了解有关线性代数运算的一些规则和注意事项。这里仅举一个例子进行说明。 在下面的例子中,先生成数值希尔伯特矩阵,然后再将它转换成符号矩阵,并对它进行 各种线性代数运算。读者可以从中体会符号对象线性代数运算的特点。示例代码如下: H = hilb(3) %生成三阶希尔伯特数值矩阵 H = sym(H) %将数值矩阵转换成为符号矩阵 inv(H) %求符号矩阵的逆矩阵 det(H) %求符号矩阵的行列式 得到的结果代码如下: 三阶希尔伯特数值矩阵为: H= 1.0000 0.5000 0.3333 0.5000 0.3333 0.2500 –328– 第 8 章 MATLAB 7.0 符号计算功能 0.3333 0.2500 0.2000 符号矩阵: H= [ 1, 1/2, 1/3] [ 1/2, 1/3, 1/4] [ 1/3, 1/4, 1/5] 符号矩阵的逆矩阵: ans = [ 9, -36, 30] [ -36, 192, -180] [ 30, -180, 180] 符号矩阵的行列式: ans = 1/2160 在上面的运算中,矩阵求逆和求行列式都采用的是符号计算,得到的结果都是精确解。 下面用指定精度来计算上面的各项,将符号计算和数值计算进行一下比较。设置代码如下: V = vpa(H,16) %用 16 位精度转换成数值矩阵 得到一定精度的矩阵: V= [ 1., .5000000000000000, .3333333333333333] [ .5000000000000000, .3333333333333333, .2500000000000000] [ .3333333333333333, .2500000000000000, .2000000000000000] 上面把矩阵的每个元素的值按照四舍五入法转换成为 16 位有效数字,这时再来求矩阵 的逆,这些舍入误差就会被放大。按照数值计算理论,放大的倍数就是矩阵的条件数。希尔 伯特矩阵是一个严重病态矩阵,它的条件数非常大(3 阶矩阵的条件数大约为 500)。所以, 误差将变的很大,这可以通过下面的计算中看到。 inv(V) %对带有误差的矩阵进行求逆 求逆的结果代码如下: ans = [9.0000000000000612, -36.00000000000032, 30.0000000000003] [-36.00000000000032, 192.00000000000166, -180.000000000001548] [30.0000000000003, -180.000000000001548, 180.00000000000144] 可见,矩阵中的最后几位均为误差。下面再来看行列式的值: det(V) %求带有误差的矩阵的行列式 行列式的值如下: ans = .4629629629629590740740740740730e-3 下面通过修改 H(1,1)的值,将 H 变成奇异矩阵,并以它为例介绍奇异矩阵的特性: syms s; H(1,1) = s %定义基本符号变量并替换 H(1,1) –329– 得到 H(1,1)的值如下: H= [ s, 1/2, 1/3] [ 1/2, 1/3, 1/4] [ 1/3, 1/4, 1/5] 再来求行列式,代码设置如下: Z = det(H) 得到新的行列式的值如下: Z= 1/240*s-1/270 再求出 Z=0 的 s 值,将它赋予 sol。 sol = solve(Z) 得到 sol 的值如下: sol = 8/9 将 sol 替换 s,得到奇异矩阵: H = subs(H,s,sol) 求新的符号矩阵的逆矩阵: inv(H) 得到结果代码如下: ??? Error using ==> sym.inv Error, (in inverse) singular matrix 由于 H 为奇异矩阵,所以求 H 的逆时将会给出错误信息。必须指出的是,虽然矩阵 H 是奇异矩阵,当采用任意精度进行计算时,无论求 H 的行列式还是逆矩阵,得到的结果均不 是 0。这就是数值计算和符号计算的根本区别。 8.5.3 特征值分解 在 MATLAB 7.0 中,分别采用下面的函数来求符号方阵的特征值和特征向量: • E = eig(A):求符号方阵 A 的符号特征值 E; • [v,E] = eig(A):返回方阵 A 的符号特征值 E 和相应的特征向量 v。 与它们对应的任意精度计算的指令是 E = eig(vpa(A))和[v,E] = eig(vpa(A))。对于上一节最 后给出的矩阵 H,由于它是奇异矩阵,肯定有一个特征值为 0: [v,E] = eig(H) %求矩阵 H 的特征值和特征向量 得到的特征值和特征向量分别如下: v= [ 1, 28/153+2/153*12589^(1/2), 28/153-2/153*12589^(1/2)] [ -4, 1, 1] [ 10/3, 292/255-1/255*12589^(1/2), 292/255+1/255*12589^(1/2)] E= –330– 第 8 章 MATLAB 7.0 符号计算功能 [ 0, 0, 0] [ 0, 32/45+1/180*12589^(1/2), 0] [ 0, 0, 32/45-1/180*12589^(1/2)] 生成的矩阵 v 和 E 中,v 的每一列就是 H 的一个特征向量,相应的 E 的对角线元素就是 H 的特征值。 8.5.4 约当标准型 对矩阵进行相似变换时,会产生约当标准型(Jordan Canonical Form)。对于矩阵 A,求 它的约当标准型,也就是找一个非奇异矩阵 V,使 J = V/A*V 最接近对角矩阵,其中的 V 称 为转换矩阵。 当矩阵对称且所有特征值均互异时,其约当标准型就是其特征值所组成的对角矩阵,相 应的转换矩阵由特征向量按列组成。对于非对称或有重特征值的情形,约当标准型的对角元 素由特征值组成,但对角线上面有非零元素。 MATLAB 7.0 提供了函数 jordan 来求矩阵的约当标准形,它的调用格式有: • J = jordan(A):计算矩阵 A 的约当标准型。其中 A 可以是数值矩阵或符号矩阵; • [V,J] = jordan(A):除了计算矩阵 A 的约旦标准型 J 外,还返回相应的变换矩阵 V。 需要特别注意的是,函数 jordan 对矩阵元素值的极微小变化均特别敏感,这使得采用数 值方法计算约当标准型非常困难,矩阵 A 的值必须精确地知道它的元素是整数或有理式(例 如,没有舍入误差)。对于任意精度矩阵是不提倡求其约当标准型的。例如下面的示例代码: A = sym([1 -3 -2;-1 1 -1;2 4 5]); %定义矩阵 [V,J] = jordan(A) %求约当标准型 得到的结果代码如下: V= [ -1, -1, 1] [ 0, -1, 0] [ 1, 2, 0] J= [ 3, 0, 0] [ 0, 2, 1] [ 0, 0, 2] 8.5.5 奇异值分解 在符号数学工具箱中只有任意精度矩阵的奇异值分解才是可行的,其中一个重要原因就 在于由符号计算产生的公式一般都太长、太复杂,而且没有太多的用处。用于对符号矩阵 A 进行奇异值分解的函数式 svd,它的调用格式如下: • S = svd(A):给出符号矩阵奇异值对角矩阵,其计算精度由函数 digits 来指定; • [U,S,V] = svd(A):输出参数 U 和 V 是两个正交矩阵,它们满足关系式:A = U*S*V’。 –331– 1 下面考虑一个矩阵 A,它的元素同时为 ,其中 i 和 j 分别为矩阵的行号和列 (i − j + 1/ 2) 号,示例代码设置如下: for i = 1:5 %用 for 循环生成 A 矩阵 for j = 1:5 A(i,j) = 1/(i-j+1/2); end end 得到 A 矩阵为: A= [ 2, -2, -2/3, -2/5, -2/7] [ 2/3, 2, -2, -2/3, -2/5] [ 2/5, 2/3, 2, -2, -2/3] [ 2/7, 2/5, 2/3, 2, -2] [ 2/9, 2/7, 2/5, 2/3, 2] 鉴于 A 中的每个元素都是有理形式,可以采用指定精度计算得到较精确的解。命令如下: digits(30) %指定输出精度 S = svd(vpa(A)) %求输入矩阵 A 的奇异值 得到的结果代码如下: S= 1.46186449332488823827862698271 2.92186083491368812533417027502 3.12952059354525758508947902807 3.14127222045650822959569061467 3.14158923834132037283159614880 8.6 符号微积分 微积分运算在数学计算中的重要性是不言自明的,整个高等数学就是建立在微积分运算 的基础上的。同时微积分运算也是后面解符号微分方程的必要知识准备。 在符号数学工具箱中提供了一些常用的函数来支持具有重要基础意义的微积分运算,涉 及的方面主要包括微分、求极限、积分、级数求和和泰勒级数等。下面来具体介绍符号运算 在微积分中的使用方法。 –332– 8.6.1 符号表达式的微分运算 第 8 章 MATLAB 7.0 符号计算功能 1.diff 函数 当创建了符号表达式后,就可以利用函数 diff 对它们进行微分运算。函数 diff 的调用格 式有 3 种,它们的形式和作用分别如下: • diff(S,’v’):将符号“v”视作变量,对符号表达式或符号矩阵 S 求取微分; • diff(S,n):将 S 中的默认变量进行 n 阶微分运算,其中默认变量可以用 findsym 函数确 定,参数 n 必须是正整数; • diff(S,’v’,n):将符号“v”视作变量,对符号表达式或矩阵 S 进行 n 阶微分运算。 例如,我们首先建立一个符号表达式,然后取相应的微分,示例代码设置如下: syms a x %定义基本变量 f = sin(a*x) %定义符号表达式 df = diff(f) %对缺省变量 x 求微分 得到的结果代码如下: df = cos(a*x)*a 再对符号表达式中的指定变量 a 求二阶微分: dfa = diff(f,a,2) 得到的结果代码如下: dfa = -sin(a*x)*x^2 2.jacobian 函数 微分运算也可以对列向量进行,所得的结果也是一个列向量。现在来考察笛卡尔坐标(x, y,z)和球坐标(r,θ,φ)的转换问题。坐标之间的转换关系为 x = r×cosθcosφ,y = r ×cosθsinφ和 z = r×sinθ。其中θ对应于仰角或纬度,而φ代表方位角或经度。在数学中, 它们之间转换的矩阵为雅可比矩阵。用函数 jacobian 可以计算这个转换矩阵 J,它的数学表 达式为 J = ∂(x, y, z) 。可见求变换矩阵的本质还是求取微分。 ∂(r,θ ,φ) 函数 jacobian 的调用格式如下: • R = jacobian(w,v):其中 w 是一个符号列向量,v 是指定进行变换的变量所组成的行 向量。 笛卡尔坐标和球坐标之间转换雅可比矩阵 J 可以用如下的命令求得: syms r l f %定义基本变量 x = r*cos(l)*cos(f); y = r*cos(l)*sin(f); %建立转换关系 z = r*sin(l) J = jacobian([x;y;z],[r l f]) %计算转换的雅可比矩阵 得到的结果代码如下: J= –333– [ cos(l)*cos(f), -r*sin(l)*cos(f), -r*cos(l)*sin(f)] [ cos(l)*sin(f), -r*sin(l)*sin(f), r*cos(l)*cos(f)] [ sin(l), r*cos(l), 0] 再来计算雅可比矩阵的行列式,并进行简化: detJ = simple(det(J)) 得到的结果代码如下: detJ = -cos(l)*r^2 注意:雅可比函数的第一个参数必须是列向量,第二个参数必须是行向量。此外,由于雅可比矩 阵的行列式值一般是比较复杂的,我们可以对这些复杂的结果进行三角函数变换和简化, 以便得到具有较为简便的形式。 8.6.2 符号表达式的极限 求微分的基本思想是当自变量趋近某个值时,求函数值的变化。“无穷逼近”是微积分 的一个基本思想,求极限是非常普遍的。事实上,导数就是由极限给出的: f ’(x) = lim f (x + h) − f (x) h→0 h 在 MATLAB 7.0 中,用函数 limit 来求表达式的极限。函数 limit 的调用格式如下: • limit(F,x,a):求当 x→a 时,符号表达式 F 的极限; • limit(F,a):符号表达式 F 采用默认自变量(可由函数 findsym 求得),该函数求 F 的自 变量趋近于 a 时的极限值; • limit(F):符号表达式 F 采用默认自变量,并以 a = 0 作为自变量的趋近值,从而求符 号表达式 F 的极限值; • limit(F,x,a,’right’)或 limit(F,x,a,’left’):分别求取符号表达式 F 的左极限和右极限,即自 变量从左边或右边趋近于 a 时的函数极限值。 例如,下面的命令分别计算 sin 函数的导数和幂函数的极限(指数函数): syms h n x %定义基本变量 dc = limit((sin(x+h)-sin(x))/h,h,0) %按照导数的定义求 sin 的导数 求得的结果代码如下: dc = cos(x) 求极限可以从两边趋近,从不同的方向趋近可能得到的结果也不相同。limit 函数第 4 种 调用方式提供了求单边极限的功能。比如,下面的 3 个表达式的值时不同的。需要注意的是, MATLAB 7.0 对于没有定义的极限将返回 NaN: 111 lim , lim , lim x x x x→0 x→0− x→0+ 用下面的命令来验证这一结论: limit(1/x,x,0) %求第一个表达式的值 –334– limit(1/x,x,0,’left’) limit(1/x,x,0,’right’) 得到的结果代码如下: ans = NaN ans = -Inf ans = Inf 第 8 章 MATLAB 7.0 符号计算功能 %求第二个表达式的值 %求第三个表达式的值 %没有意义,返回 NaN %结果为负无穷 %结果为正无穷 8.6.3 符号表达式的积分 数学中,积分和微分是一对互逆的运算。符号数学工具箱种提供了函数 int 来求符号表 达式的积分,它的调用格式如下: • R = int(S):用缺省变量求符号表达式 S 的不定积分,缺省变量可用函数 findsym 确定; • R = int(S,v):用符号标量 v 作为变量求符号表达式 S 的不定积分值; • R = int(S,a,b):符号表达式采用缺省变量,该函数求缺省变量从 a 变到 b 时符号表达式 S 的定积分值。如果 S 是符号矩阵,那么积分将对各个元素进行分别进行,而且每个 元素的变量也可以独立地由函数 findsym 来确定,a 和 b 可以是符号或数值标量; • R = int(S,v,a,b):符号表达式采用符号标量 v 作为标量,求当 v 从 a 变到 b 时,符号表 达式 S 的定积分值。其他参数和上一种调用方式相同。 例如,下面用各种调用方式求符号表达式和符号矩阵的积分值,首先是用缺省变量对符 号表达式求积分。 int(-2*x/(1+x^2)^2) %对符号表达式进行积分 得到的结果代码如下: ans = 1/(1+x^2) 和微分运算比起来,积分的运算困难要大得多。当函数和积分不存在时,MATLAB 7.0 将简单地返回原来的积分表达式。例如,下面的命令试图求贝赛尔函数的积分: a = int(besselj(l,z),0,1) %求贝赛尔函数的积分 得到的结果代码如下: a= -besselj(0,1)+1 %积分失败,返回原表达式 在符号数学工具箱中,常数是积分时一个比较敏感的问题。例如表达式 e−(kx)2 ,当 k 为 实数时,该表达式的值永远为正,并且当 x 的值趋于正负无穷时,该表达式的值趋于 0。但 是 Maple 的内核并不把 k2 或 x2 看作正数,而只是简单地假定它们为符号,其值不定,没有丝 毫的数学属性。用下面的命令计算该积分,将给出错误信息: syms x k; %定义积分变量 f = exp(-(k*x)^2) %定义积分函数 –335– int(f,x,-inf,inf) %积分该函数 得到的结果代码如下: ans = PIECEWISE([csgn(k)/k*pi^(1/2), csgn(k^2) = 1],[Inf, otherwise]) 从上面的结果我们发现,当 k^2 大于 0 的情况下,积分可以得到,而当其他情况下积分 为无穷大。因此,在计算前,应当首先定义 k 为实变量,这样才能得到正确的结果: syms k real %定义 k 为实变量 int(f,x,-inf,inf) %重新积分 f 函数 得到的结果代码如下: ans = signum(k)/k*pi^(1/2) 注意:上面的 k 既是 MATLAB 7.0 工作控件中的变量,同时也是 Maple 内核工作空间中的一个实 变量,命令“clear k”只能从 MATLAB 7.0 的工作空间中删除符号对象 k,但在 Maple 内 核空间中还存在实变量 k。可以利用“syms k unreal”命令从 Maple 内核空间中删除 k。 8.6.4 级数的求和 函数 symsum 用于对符号表达式进行求和。该函数的调用格式如下: • r = symsum(s,a,b):求符号表达式 s 中默认变量从 a 变到 b 时的有限和; • r = symsum(s,v,a,b):求符号表达式 s 中变量 v 从 a 变到 b 时的有限和。 具体的示例代码如下: syms x k %定义基本变量 s1 = symsum(1/k^2,1,inf) %求无穷级数的和 s1 = symsum(x^k,k,0,inf) %求无穷级数的和 得到的结果代码如下: s1 = 1/6*pi^2 s1 = -1/(x-1) 8.6.5 泰勒级数 函数 taylor 用来求符号表达式的泰勒级数展开式,该函数的调用格式如下: • r = taylor(f):f 是符号表达式,其变量采用默认变量,该函数将返回 f 在变量等于 0 处 作 5 阶泰勒展开时的展开式; • r = taylor(f,n,v):符号表达式 f 以符号标量 v 作为自变量,返回 f 的 n-1 阶麦克劳林级 数(即在 v = 0 处作泰勒展开)展开式; • r = taylor(f,n,v,a):返回符号表达式 f 在 v = a 处作 n-1 阶泰勒展开的展开式。 例如,下面的命令将返回函数的 8 阶泰勒展开式: –336– 第 8 章 MATLAB 7.0 符号计算功能 syms x %定义基本符号变量 f = 1/(2+cos(x)) %定义 f 函数 r = taylor(f,8) %以 x 为自变量求 f 的泰勒展开式 得到的结果代码如下: r= 1/3+1/18*x^2+1/216*x^4+1/6480*x^6 下面的命令将给出函数在 x = 2 处展开的前 3 个非零项: g = exp(x*sin(x)) %定义函数 t = taylor(g,3,2) %求 x = 2 处的泰勒展开式 得到的结果代码如下: t= exp(2*sin(2))+exp(2*sin(2))*(2*cos(2)+sin(2))*(x-2)+exp(2*sin(2))*(-sin(2)+cos(2)+2*cos(2)^2+2*cos(2)*s in(2)+1/2*sin(2)^2)*(x-2)^2 下面的命令将求出函数 sin(x)*e-x 的麦克劳林级数: ft = taylor(sym(sin(x)*exp(-x)),8) %求函数的麦克劳林级数 得到的结果代码如下: ft = x-x^2+1/3*x^3-1/30*x^5+1/90*x^6-1/630*x^7 8.7 符号积分变换 在数学中,为了把较复杂的运算转化为比较简单的运算,经常采用一种变换手段。例如 数量的乘积或商可以通过对数学变换成对数的和或差,然后再取反对数即可求得原来数量的 乘积或商。这一变换方法的目的就是把比较复杂的乘除运算通过对数变换转化为简单的加减 运算。 所谓积分变换,就是通过积分运算,把一类函数 A 变换成另一类函数 B,函数 B 一般是 b ∫ 含有参量α的积分 f (t)K (t,α )dt 。这一变换的目的,就是把某函数类 A 中的函数 f(t)通过 a 积分运算变成另一类函数 B 中的函数 F(α)。这里 K(t,α)是一个确定的二元函数,叫做积分 变换的核。当选取不同的积分区间与变换核时,就成为不同的积分变换。f(t)叫做原函数,F(α) 叫做象函数。在一定条件下,原函数与象函数两者一一对应,成为一个积分变换对。变换时 可逆,有原函数求象函数叫做正变换,反之则是逆变换。 积分变换的理论与方法,在自然科学与工程技术的各个领域中都有着极广泛的应用,成 为不可缺少的运算工具。所有变换的使用,都会极大地简化计算,有的变换则为开创新的学 科奠定了基础。 以下要介绍 3 种积分变换,即 Fourier 变换、Laplace 变换与 Z 变换。 –337– 8.7.1 Fourier 变换 时域中的 f(t)与它在频域中的 Fourier 变换 F(ω)之间存在如下关系: ∞ ∫ F (ω ) = f (t)e− jωt dt −∞ ∫ f (t) = 1 ∞ F (ω )e jwt dω 2π −∞ 由计算机完成这种变换的途径有两条:一种是直接调用指令 fourier 和 ifourier 进行;另 一种是根据上面的定义,利用积分指令 int 实现。下面只介绍 fourier 和 ifourier 的使用及相关 注意事项。至于根据定义求变换,请读者自己完成。 • Fw = fourier(ft,t,w):求时域函数 ft 的 Fourier 变换 Fw,ft 是以 t 为自变量的时域函数, Fw 是以圆频率 w 为自变量的频域函数; • ft = ifourier(Fw,w,t):求频域函数 Fw 的 Fourier 反变换,ft 是以 t 为自变量的时域函数, Fw 是以圆频率 w 为自变量的频域函数。 下面的示例代码将给出 Fourier 变换的具体实现: syms t w %定义基本符号变量 ut = Heaviside(t); %定义 0 时刻起跳的单位阶跃函数 UT = fourier(ut) %进行 fourier 变换 得到的结果代码如下: UT = pi*dirac(w)-i/w 再来求 Fourier 反变换: Ut = ifourier(UT,w,t) %进行 fourier 反变换 得到的结果代码如下: Ut = heaviside(t) 同原先的时域函数相同。 注意:在 MATLAB 7.0 的符号数学工具箱中增加了 dirac 和 heaviside 两个函数,它们分别是单位 脉冲函数和阶跃函数。在老版本的 MATLAB 中,如果需要调用这两个函数,则必须从 Maple 函数库中调用,因为原先的 MATLAB 本身没有这样的函数提供。 8.7.2 Laplace 变换 Laplace 变换和反变换的定义为: ∞ ∫ F (s) = f (t)e−st dt 0 –338– 第 8 章 MATLAB 7.0 符号计算功能 ∫ f (t) = 1 c+ j∞ F (s)e st ds 2πj c− j∞ 与 Fourier 变换相似,Laplace 变换与反变换的实现也有两条途径。直接调用指令 laplace 和 ilaplace 进行,或者根据上面的定义,利用积分指令 int 实现。比较而言,直接使用 laplace 和 ilaplace 指令实现变换较为简洁。具体使用方法如下: • Fs = laplace(ft,t,s):求时域函数 ft 的 Laplace 变换 Fs,ft 是以 t 为自变量的时域函数, Fs 是以复频率 s 为自变量的频域函数; • ft = ilaplace(Fs,s,t):求频域函数 Fs 的 Laplace 反变换 ft,ft 是以 t 为自变量的时域函数, Fs 是以复频率 s 为自变量的频域函数。 下面的例子给出 Laplace 变换的具体实现:  δ (t − a) u(t − b)  求 e−at sin bt t 2 cos 3t 的 Laplace 变换。 syms t s %定义基本符号变量 syms a b positive %对常数进行“限定性”设置 Mt = [dirac(t-a),heaviside(t-b);exp(-a*t)*sin(b*t),t^2*cos(3*t)]; %定义输入矩阵 MS = laplace(Mt,t,s) %进行 Laplace 变换 得到的结果代码如下: MS = [ exp(-s*a), exp(-s*b)/s] [ 1/b/((s+a)^2/b^2+1), 2/(s^2+9)^3*(s^3-27*s)] 8.7.3 Z 变换 一个离散因果序列的 Z 变换及其反变换定义为: ∞ ∑ F (z) = f (n)z −n n=0 f (n) = Z −1{F (z)} 涉及 Z 反变换具体计算的方法,最常见的有 3 种,分别是幂级数展开法、部分分式展开 法和围线积分法。MATLAB 7.0 的符号数学工具箱中采用了围线积分法设计了求取 Z 反变换 ∫ 的 iztrans 指令,相应的数学表达式是 f (n) = 1 F (z)z n−1dz 。具体的命令格式如下: 2πj T • FZ = ztrans(fn,n,z):求时域函数 fn 的 Z 变换 FZ,fn 是以 n 为自变量的时域序列,FZ 是以复频率 z 为自变量的频域函数; • fn = iztrans(FZ,z,n):求频域函数 FZ 的 Z 反变换 fn,fn 是以 n 为自变量的时域序列, FZ 是以复频率 z 为自变量的频域函数。 –339– 下面的例子给出 Laplace 变换的具体实现: 0 n<0 求序列 f (n) =  2 n = 0 的 Z 变换,并用反变换验算。 6(1 − 0.5n ) n > 0 示例代码如下: %首先来构造单位函数并对其性能进行验证 syms n Delta = sym(’charfcn[0](n)’); %定义单位函数δ(n) D0 = subs(Delta,n,0); %计算δ(0) D15 = subs(Delta,n,15); %计算δ(15) disp(’[D0,D15]’); disp([D0,D15]) 得到的结果代码如下: [D0,D15] [ 1, 0] 再来求序列 f(n)的 Z 变换,示例代码如下: syms z fn = 2*Delta+6*(1-(1/2)^n); FZ = ztrans(fn,n,z); disp(’FZ = ’); pretty(FZ) 得到的结果代码如下: FZ = z z 2 + 6 ----- - 12 ------- z-1 2z–1 最后 Z 反变换,示例代码如下: FZ_n = iztrans(FZ,z,n) 得到的结果代码如下: FZ_n = 2*charfcn[0](n)+6-6*(1/2)^n 说明:在输入函数的定义上,我们引入了一个函数 charfcn,需要说明的是,这个函数是 Maple V5 中的表达式的指征函数(Characteristic function),它满足 charfcn[ A]( x) = 1 0 x∈ A 。 x∉ A –340– 8.8 符号方程求解 第 8 章 MATLAB 7.0 符号计算功能 从学习代数开始,我们就一直在探索关于方程的求解理论,从最初的代入消元法和加减 消元法到数值计算中的牛顿迭代法、高斯消元法,一直到微分方程的求解理论,方程在数学 中的重要性就包含在这么漫长的探索、深化过程中。MATLAB 7.0 为符号方程的求解提供了 强有力的支持。 符号方程根据其中涉及到的运算类别,可以分为代数方程和微分方程。其中代数方程只 涉及到符号对象的代数运算,相对比较简单,它还可以细分为线性方程和非线性方程两类。 前者往往可以很容易地求得所有解,但是对于后者来说,却经常容易丢掉一些解,这时就必 须借助绘制函数图形,通过图形来判断方程解的个数。 微分方程的求解稍微复杂一些,它按照自变量的个数,可以分为常微分方程和偏微分方 程。偏微分方程的求解在数学上相当复杂,而且理论体系也繁杂,用机器求解往往不能找到 通行的方法,所以这里介绍用 MATLAB 7.0 求解常微分方程。 8.8.1 代数方程的求解 这里所讲的一般代数方程包括线性、非线性和超越方程等,求解指令是 solve。当方程组 不存在符号解时,若又无其他自由参数,则 solve 将给出数值解。该指令的使用格式包括以 下几种: • g = solve(eq):其中 eq 可以是符号表达式或不带符号的字符串,该函数将求解方程 eq = 0,其自变量采用默认变量,可以通过函数 findsym 来确定; • g = solve(eq,var):求解方程 eq = 0,其自变量由参数 var 指定。其中 eq 和上一种调用 方式相同。返回值 g 是由方程的所有解构成的列向量; • g = solve(eq1,eq2,…,eqn):求解由符号表达式或不带符号的字符串 eq1,eq2,…,eqn 组成的方程组。其中的自变量为整个方程组的默认变量,即将函数 findsym 作用于整 个方程组时返回的变量; • g = solve(eq1,eq2,…,eqn,var1,var2,…,varn):求解由符号表达式或不带等号的字符串 eq1,eq2,…,eqn 组成的方程组。其自变量由输入参数 var1,var2,…,varn 指定。 对于上面的 4 种调用方式,输出的解有 3 种情况: • 对于单个方程单个输出参数的情况,将返回由多个解构成的列向量; • 对于有和方程数目相同的输出参数的情况,方程组的解将分别赋给每个输出参数,并 按照字母表的顺序进行排列; • 对于只有一个输出参数的方程组,方程组的解将以结构矩阵的形式赋给输出参数。 下面给出一个代数方程的求解过程: uy 2 + vz + w = 0  求方程组  关于 y,z 的解。  y + z + w = 0 S = solve(’u*y^2+v*z+w = 0’,’y+z+w = 0’,’y’,’z’) disp(’S.y’),disp(S.y),disp(’S.z’),disp(S.z) –341– 得到解如下: S= y: [2x1 sym] z: [2x1 sym] %显示 y 的解 S.y -1/2/u*(-2*u*w-v+(4*u*w*v+v^2-4*u*w)^(1/2))-w -1/2/u*(-2*u*w-v-(4*u*w*v+v^2-4*u*w)^(1/2))-w %显示 z 的解 S.z 1/2/u*(-2*u*w-v+(4*u*w*v+v^2-4*u*w)^(1/2)) 1/2/u*(-2*u*w-v-(4*u*w*v+v^2-4*u*w)^(1/2)) 注意:需要注意的是如果没有指定变量,findsym 将把 w,y 依次作为自变量,最终的结果将与现 在不同,读者可以自己尝试。 8.8.2 微分方程的求解 在前面已经讲过:从数值计算角度看,与初值问题求解相比,微分方程边值问题的求解 显得复杂和困难。对于应用数学工具去求解实际问题的科研人员来说,此时,不妨通过符号 计算指令进行求解尝试。因为,对于符号计算来说,不论是初值问题,还是边值问题,其求 解微分方程的指令形式都相同,且相当简单。 当然,符号计算可能花费较多的计算机资源,可能得不到简单的解析解或封闭形式的解, 甚至无法求解。既然没有万能的微分方程一般解法,那么:求解微分方程的符号法和数值法 有很好的互补作用。 函数 dsolve 用来求常微分方程的符号解。在方程中,用大写字母 D 表示一次微分,D2、 d2y D3 分别表示二次、三次微分运算。以此类推,符号 D2y 表示 dt 2 。函数 dsolve 把 d 后面的 字符当作因变量,并默认所有这些变量对符号 t 进行求导。函数 dsolve 的调用方式有: • r = dsolve(’eq1,eq2,…’,’cond1,cond2,…’,’v’):求由 eq1,eq2,…指定的常微分方程的符 号解。常微分方程以变量 v 作为自变量,参数 cond1,cond2,…用于指定方程的边界 条件或者初始条件。如果 v 不指定,将默认 t 为自变量; • r = dsolve(’eq1’,’eq2’,…,’cond1’,’cond2’,…,’v’):求由 eq1,eq2,…指定的常微分方程的符 号解。这些常微分方程都以 v 作为自变量。这些单独输入的方程的最大允许个数为 12。 其他参数与上一种调用方式相同。 微分方程的初始条件或边界条件都以变量 v 作为自变量,其形式为 y(a) = b 或 Dy(a) = b ,其中 y 是微分方程的因变量,a 和 b 是常数。如果指定的初始条件和边界条件 –342– 第 8 章 MATLAB 7.0 符号计算功能 比方程中的因变量个数少,那么所得的解中将包含积分常数 C1、C2 等。 函数 dsolve 的输出结果同 solve 类似,既可以用和因变量个数相同数目的输出参数分别 接收每个变量的解,也可以把方程的解写入一个结构数组中。 下面举两个常微分方程求解的例子: 求 dx = y , dy = −x 的解。示例代码如下: dt dt S = dsolve(’Dx = y,Dy = -x’); disp([blanks(12),’x’,blanks(21),’y’]),disp([S.x,S.y]) 得到的结果代码如下: x y [ -C1*cos(t)+C2*sin(t), C1*sin(t)+C2*cos(t)] 求解两点边值问题: xy’’−2 y’= x 2 , y(1) = 0 , y(2) = 5 。 y = dsolve(’x*D2y-2*Dy = x^2’,’y(1) = 0,y(5) = 0’,’x’) 得到的结果代码如下: y= 1/3*x^3*log(x)-1/9*x^3+1/3*(1/3-125/124*log(5))*x^3+125/372*log(5) 注意:在本例程中如果用数值解法,则比较复杂,有兴趣的读者可以自己尝试。 8.9 可视化数学分析界面 MATLAB 7.0 的符号数学工具箱为符号函数可视化提供了一组简便易用的指令。本章着 重介绍两个进行数学分析的可视化界面,即图示化符号函数计算器(由指令 funtool 引出)和 泰勒级数逼近分析界面(由指令 taylortool 引出)。 8.9.1 图示化符号函数计算器 对于习惯使用计算器或者只是向作一些简单的符号运算与图形处理的读者,MATLAB 7.0 提供的图示化符号函数计算器是一个较好的选择。该计算器功能虽简单,但操作方便,可 视性强。 –343– 图 8-1 图示化符号函数计算器界面 在如图 8-1 所示的图示化符号函数计算器是由 2 个图形窗口(Figure No.1 和 Figure No.2) 与一个函数运算控制窗口(Figure No.3)3 个独立窗口组成。在任何时候,两个图形窗口只 有一个处于激活状态。函数运算控制窗口上的任何操作都只能对被激活的函数图形窗口起作 用,即被激活的函数图像可随运算控制窗口的操作而作相应的变化。 (1)第 1 排按键只对 f 起作用,如求导、积分、简化、提取分子和分母、计算 1/f 以及求 反函数。 (2)第 2 排按键处理函数 f 和常数 a 之间的加、减、乘、除等运算。 (3)第 3 排的前 4 个按键对两个函数 f 和 g 之间进行算术运算。第 5 个按键求复合函数, 第 6 个按键的功能是把 f 函数传递给 g,最后一个按键 swap 是实现 f 和 g 的互换。 (4)第 4 排按键用于对计算器自身进行操作。funtool 计算器有一张函数列表 fxlist。这 7 个按键的功能依次是: • Insert:把当前激活窗的函数写入列表; • Cycle:依次循环显示 fxlist 中的函数; • Delete:从 fxlist 列表中删除激活窗的函数; • Reset:使计算器恢复到初始调用状态; • Help:获得关于界面的在线提示说明; • Demo:自动演示。 –344– 8.9.2 泰勒级数逼近分析器 第 8 章 MATLAB 7.0 符号计算功能 在 MATLAB 7.0 指令窗口中运行以下指令,将引出如图 8-2 所示的泰勒逼近分析界面。 taylortool 图 8-2 泰勒级数逼近分析界面 • 该界面用于观察函数 f(x)在给定区间上被 N 阶泰勒多项式 TN(x)逼近的情况; • 函数 f(x)的输入方式有两种,直接由指令 taylortool(fx)引入,或者在界面的 f(x)栏中直 接键入表达式,然后回车输入; • 界面中 N 被缺省地设置为 7,可以用其右侧的按键改变阶次,也可以直接写入阶次; • 界面上的 a 是级数的展开点,缺省值为 0; • 函数的观察区被缺省地设置为(-2π,2π)。 8.10 Maple 接口 相对于 Maple 中的 2000 多条符号计算指令而言,前面介绍的函数只是其中的一小部分。 为了在 MATLAB 7.0 中进一步利用 Maple 的符号计算能力,要求用户必须对 Maple 有较深的 理解。符号数学工具箱为此提供了两个函数,即 sym 和 maple。 sym 和 maple 这两个指令可以实现对 Maple 绝大多数符号计算指令的调用,利用它们可 以大大扩充 MATLAB 7.0 的符号计算功能。下面将通过示例分别介绍两个指令的使用。 8.10.1 利用 sym 函数调用 Maple 函数 例如,通过函数 sym 来调用 Maple 的阶乘函数 k!,代码设置如下: –345– kfac = sym(’k!’); %调用 Maple 函数 这相当于定义了一个可以用来计算正整数阶乘的符号函数。利用它来计算一下阶乘 7! 和 n!,相应的命令如下: syms k n %定义基本变量 subs(kfac,k,7) %计算阶乘 subs(kfac,k,n) %计算阶乘 得到的结果代码如下: ans = 5040 ans = n! 8.10.2 利用 maple 函数调用 Maple 函数 采用 maple 函数可以直接调用 Maple 中的函数。这个函数采用符号对象、字符串和双精 度数作为输入参数,返回和输入参数对应的符号对象、字符串和双精度数。也可以利用函数 maple 来测试用户自己编写的符号数学程序。 假定要写一个 M 文件,用来计算两个多项式和两个整数的最大公约数。为此,首先要知 道怎样调用 Maple 中求取最大公约数的函数。可以使用命令 mhelp 来获得这方面的信息。输 入命令“mhelp gcd”即可获得 Maple 中用于求取最大公约数的函数 gcd 的相关信息。 当已经知道了如何调用函数 gcd 后,就可以编写一个简单的 M 文件 gcd.m 来调用这个 Maple 函数,从而轻松地求得两个多项式或整数的最大公约数。M 文件的内容如下: function g = gcd(a,b) g = maple(’gcd’,a,b); 这样就可以简单地调用该文件,并利用 Maple 函数求最大公约数。示例代码如下: syms x y %定义基本变量 z = gcd(x^2-y^2,x^3-y^3) %求多项式的最大公约数 得到的结果代码如下: z= -y+x 可见,利用 maple 函数直接调用 Maple 中的函数,将大大地扩充 MATLAB 7.0 的符号计 算功能。如果读者有什么特殊的符号计算要求,而不能由 MATLAB 7.0 来完成,不妨通过调 用 Maple 函数来解决。 –346– 第 9 章 文件 I/O MATLAB 系统具有直接对磁盘文件进行访问的功能,用户不仅可进行高层的程序设计, 必要时还可进行低层次磁盘文件的读写操作,增强了 MATLAB 程序设计的灵活性。 MATLAB 内建有很多有关文件输入和输出的函数,使用者可以很方便地对二进制文件或 ASCII 文件进行打开、关闭和存储等操作。这些函数是基于 C 语言的文件 I/O 函数的,简单 易学。 9.1 打开和关闭文件 9.1.1 打开文件 根据操作系统的要求,在程序要使用或者创建一个磁盘文件时,必须向操作系统发出打 开文件的命令,使用完毕后,还必须通知操作系统关闭这些文件。 在 MATLAB 中,使用 C 语言中的同名函数 fopen 来完成这一功能,其语法为: fid=fopen(’filename’,’permission’) 其中 filename 是要打开的文件名称,permission 则表示要对文件进行处理的方式。可以 是下列任一字符串: • ’r’:只读文件(reading); • ’w’:只写文件,覆盖文件原有内容(如果文件名不存在,则生成新文件,writing); • ’a’:增补文件,在文件尾增加数据(如果文件名不存在,则生成新文件,appending); • ’r+’:读写文件(不生成文件,reading and writing); • ’w+’:创建一个新文件或者删除已有文件内容,并可进行读写操作; • ’a+’:读取和增补文件(如果文件名不存在,则生成新文件)。 文件可以以二进制的形式或者文本形式打开(默认情况下是前者),在二进制形式下, 字符串不会被特殊对待。如果要求以文本形式打开,则在 permission 字符串后面加’t’,例如’rt+’, ’wt+’等。需要说明的是在 UNIX 下,文本形式和二进制形式没有什么区别。 fid 是一个非负整数,称为文件标识,对于文件的任何操作,都是通过这个标识值来传递 的,MATLAB 通过这个值来标识已打开的文件,实现对文件的读、写和关闭等操作。正常情 况下应该返回一个非负的整数,这个值是由操作系统设定的。如果返回的文件标识为’-1’,则 表示 fopen 无法打开该文件,原因可能是该文件不存在,而以’r’或’r+’方式打开,也可能是用 户无权限打开此文件。程序设计中,每次打开文件,都要进行打开操作是否正确的测定。如 果要知道 fopen 操作失败的原因,可以使用下列方式调用: [fid,message]=fopen(’filename’,’r’) MATLAB 7.0 输出变量 message 中包含了文件打开操作结果的信息,示例代码设置如下: [fid,message]=fopen(’test.dat’,’r’); if fid==-1 disp(message); end 运行后在命令行显示为: No such file or directory 如果是打开一个存在的文件,示例代码设置如下: [fid,message]=fopen(’sum2.m’,’r’); if fid==-1 disp(message); end 运行后分别查看 message 和 fid 的值如下: >> message message = ’’ >> fid fid = 3 文件标识 fid=1 和 fid=2 是自动获取的,不必打开。如果要获取所有已打开的文件标识 值,可用以下命令: >> fids=fopen(’all’) 注意,查看错误信息时,也可用 MATLAB 的函数 ferror,其函数格式如下: message=ferror(fid) 9.1.2 关闭文件 在进行完读写操作后,必须关闭文件,以免打开文件过多,造成系统资源浪费,命令为: >> status=fclose(fid) 如果关闭成功,则返回 status 值为 0;否则返回-1。 上述命令是关闭了文件标识为 fid 的文件,如果要一次关闭所有打开的文件,则需执行 下面的代码: >> status=fclose(’all’) 用户可以通过检查 status 的值来确认文件是否关闭。 在某些情况下,可能需要用到暂存目录及临时文件,要取用系统的暂存目录,可用 tempdir 命令: >> directory=tempdir directory = C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ 要打开一个临时文件,可用 tempname 命令: –348– >> filename=tempname filename = C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\tp145834 >> fid=fopen(filename,’w’); 第 9 章 文件 I/O 注意:打开和关闭文件的操作都比较费时,尽量不要将它们置于循环中,以提高程序执行的效率。 9.2 读取二进制文件 MATLAB 中函数 fread 可以从文件中读取二进制数据,将每一个字节看成一个整数,将 结果写入一个矩阵返回。最基本的调用形式为: a=fread(fid) 其中,fid 是从 fopen 中得来的文件标识。MATLAB 读取整个文件并将文件指针放在文件 末尾处(在后面的 feof 命令介绍中有详细解释)。 例如,文件 t_b.m 内容如下: a=[15,20,25,30,35]; b=[1554.88,1555.24,1555.76,1556.20,1556.68]; figure(1) plot(a,b) 用 fread 函数读取此文件,操作如下: >> fid=fopen(’t_b.m’,’r’); >> data=fread(fid); 如要验证,可输入如下代码: >> disp(char(data’)) a=[15,20,25,30,35]; b=[1554.88,1555.24,1555.76,1556.20,1556.68]; figure(1) plot(a,b) 如果不用 char 将 data 转换为 ASCII 字符,则输出的是一组整数,取 data 的转置是为了 方便阅读。 函数 fread 返回矩阵的大小和形式是可控的,通过 fread 的第 2 个输入变量来实现: a=fread(fid,size) size 的有效输入大体可分为 3 种: • n:读取前 n 个整数,并写入一个列向量中; • inf:读至文件末尾; • [m,n]:读取数据到 m×n 的矩阵中,按列排序,n 可以是 inf,m 不可以。 例如,对 t_b.m 文件进行操作,示例代码设置如下: >> fid=fopen(’t_b.m’,’r’); >> data=fread(fid,4) –349– MATLAB 7.0 data = 97 61 91 49 >> data1=fread(fid,[3,2]) data1 = 53 48 44 44 50 50 其中,后一个 fread 命令读取的数据是紧接第一个 fread 的。 fread 命令还有第 3 个输入变量,用来控制二进制数据转换成 MATLAB 矩阵时所用的精 度,命令格式为: a=fread(fid,size,precision) precision 包括两部分:一是数据类型定义,比如 int、float 等;二是一次读取的位数。默 认情况下是 uchar(8 位字符型),常用的精度在表 9-1 中有简单的介绍,并且与 C 和 Fortran 中相当的形式做了对比。 表 9-1 精度类型 MATLAB C 或 Fortran 描述 ’uchar’ ’unsigned char’ 无符号字符型 ’schar’ ’signed char’ 带符号字符型(8 位) ’int8’ ’integer*1’ 整型(8 位) ’int16’ ’integer*2’ 整型(16 位) ’int32’ ’integer*4’ 整型(32 位) ’int64’ ’integer*8’ 整型(64 位) ’uint8’ ’integer*1’ 无符号整型(8 位) ’uint16’ ’integer*2’ 无符号整型(16 位) ’uint32’ ’integer*4’ 无符号整型(32 位) ’uint64’ ’single’ ’integer*8’ ’real*4’ 无符号整型(64 位) 浮点数(32 位) ’float32’ ’real*4’ 浮点数(32 位) ’double’ ’real*8’ 浮点数(64 位) ’float64’ ’real*8 浮点数(64 位) 还有一些类型是与平台有关的,平台不同可能位数不同,如表 9-2 所示。 表 9-2 MATLAB ’char’ ’short’ ’int’ ’long’ ’ushort’ ’uint’ ’ulong’ ’float’ 与平台有关的精度类型 C 或 Fortran ’char*1’ ’short’ ’int’ ’long’ ’unsigned short’ ’unsigned int’ ’unsigned long’ ’float’ 描述 字符型(8 位,有符号或无符号) 整型(16 位) 整型(32 位) 整型(32 位或 64 位) 无符号整型(16 位) 无符号整型(32 位) 无符号整型(32 位或 64 位) 浮点数(32 位) –350– 第 9 章 文件 I/O 如果 fid 表示的是已打开的浮点类型的数据文件,则得到下面的代码: >> a=fread(fid,10,’float’) 将读入前 10 个浮点型数据到列向量 a 中。 注意:fread 还有两个输入参数,即 skip 和 machineformat,均不常用,其用法可参考 MATLAB 中 fread 的 help 文件。 9.3 写入二进制文件 函数 fwrite 的作用是将一个矩阵的元素按所定的二进制格式写入某个打开的文件,并返 回成功写入的数据个数。格式为: count=fwrite(fid,a,precision) 其中,fid 是从 fopen 得到的文件标识,a 是待写入的矩阵,precision 设定了结果的精度, 可用的精度类型见 fread 中的叙述。示例代码设置如下: >> fid=fopen(’tob.bin’,’w’); >> count=fwrite(fid,magic(5),’int32’); >> status=fclose(fid) 上面的程序段的执行结果是生成一个文件名为 tob.bin 的二进制文件,长度为 100 字节, 包含 5×5 个数据,即 5 阶方阵的数据,每个数据占用 4 个字节的存储单位,数据类型为整型, 输出变量 count 值为 25。由于是二进制文件,所以无法用 type 命令来显示文件内容,如果要 查看,可用以下命令: >> fid=fopen(’tob.bin’,’r’) fid = 4 >> data=(fread(fid,25,’int32’))’ data = 17 23 4 10 11 24 5 6 12 18 1 7 13 19 25 8 14 20 21 2 15 16 22 3 9 9.4 读取文本文件 如果需要将文本文件中的某一行读出,并将该行的内容以字符串形式返回,可采用以下 两个命令: tline=fgetl(fid) tline=fgets(fid) 两个函数的功能很相似,均可从文件中读取一行数据,区别在于 fgetl 会舍弃换行符,而 fgets 则保留换行符。示例代码设置如下: fid=fopen(’fgetl.m’); –351– MATLAB 7.0 while 1 tline = fgetl(fid); if ~ischar(tline) break; end disp(tline) end fclose(fid); 命令执行结果为: function tline = fgetl(fid) %FGETL Read line from file, discard newline character. % TLINE = FGETL(FID) returns the next line of a file associated with file % identifier FID as a MATLAB string. The line terminator is NOT % included. Use FGETS to get the next line with the line terminator % INCLUDED. If just an end-of-file is encountered then -1 is returned. % FGETL is intended for use with text files only. Given a binary file % with no newline characters, FGETL may take a long time to execute. % Example % fid=fopen(’fgetl.m’); % while 1 % tline = fgetl(fid); % if ~ischar(tline), break, end % disp(tline) % end % fclose(fid);% % See also FGETS, FOPEN. % Copyright 1984-2003 The MathWorks, Inc. % $Revision: 5.15.4.2 $ $Date: 2004/04/10 23:29:22 $ try [tline,lt] = fgets(fid); tline = tline(1:end-length(lt)); if isempty(tline) tline = ’’; end catch if nargin ~= 1 error (nargchk(1,1,nargin,’struct’)) end if isempty(fopen(fid)) error (’MATLAB:fgetl:InvalidFID’,’Invalid file identifier.’) –352– 第 9 章 文件 I/O end rethrow(lasterror) end 若已知 ASCII 文件的格式,要进行更精确的读取,可用 fscanf 函数从文件中读取格式化 的数据,其使用语法如下: [a,count]=fscanf(fid,format,size) 此命令从文件标识为 fid 的文件中读取数据,并转换成指定的 format 格式字符串,返回 到矩阵 a 中。count 是可选输出项,表示成功读取的数据个数。size 是可选输入项,它对可以 从文件读取的数据数目做了限制,如果没有指定,则默认为整个文件,否则可以指定为 3 种 类型:n,inf,[m,n]。各项的意义同 9.2 节中的描述。 format 用于指定读入数据的类型,常用的格式有: • %s:按字符串进行输入转换; • %d:按十进制数据进行转换; • %f:按浮点数进行转换。 另外还有其他的格式,它们与 C 语言中的 fprintf 中参数的用法是相同的,可以参阅 C 手 册。 在格式说明中,除了单个的空格字符可以匹配任意个数的空格字符外,通常的字符在输 入转换时将与输入的字符一一匹配,函数 fscanf 将输入的文件看作是一个输入流,MATLAB 根据格式来匹配输入流,并将在流中匹配的数据读入到 MATLAB 系统中。下面是一个文件 输入的例子: >> fid=fopen(’table.txt’,’r’); >> title=fscanf(fid,’%s’); >> status=fclose(fid); 读取的数据解释为字符串,结果为: title = 1491215262034234676 用 type 命令读取 table.txt 文件内容为: >> type table.txt 1 4 9 12 15 26 20 34 23 46 76 如果以双精度格式来读取的话,则程序和结果为: >> fid=fopen(’table.txt’,’r’) >> data=fscanf(fid,’%f’) data = 1 4 9 12 15 26 –353– MATLAB 7.0 20 34 23 46 76 上例显示了 MATLAB 的 fscanf 函数和 C 中 fscanf 函数的不同之处。MATLAB 的 fscanf 函数是向量化的,只要读入数据的类型正确,MATLAB 的 fscanf 函数会一再执行,并把所得 结果以向量形式返回。 如果要对返回向量的形式和大小进行限制,可以加入 size 输入变量。示例代码设置如下: >> fid=fopen(’table.txt’,’r’,[3,4]); >>data=fscanf(fid,’%f’,[3,4]); >> status=fclose(fid); data = 1 12 20 46 4 15 34 76 9 26 23 0 注意:(1)fscanf 在读取文件时,是逐行进行的,在返回矩阵时,是将数据逐列写入的。(2)本 节所介绍的命令不能对二进制文件进行操作。(3)sscanf 函数和 fscanf 函数的功能类似, 不同的是 sscanf 是从字符串中读取数据,而不是对文件的操作。 9.5 写入文本文件 MATLAB 的函数 fprintf 的作用是将数据转换成指定格式字符串,写入到文本文件中。其 语法格式为: count=fprintf(fid,format,y) 其中 fid 是要写入的文件标识,由 fopen 产生,format 是格式指定字符串,用以指定数据 写至文件的格式,y 是 MATLAB 的数据变量。count 是返回的成功写入的字节数。 fid 值也可以是代表标准输出的 1 和代表标准出错的 2,如果 fid 字段省略,则默认值为 1, 输出到屏幕上。常用的格式类型说明符如下: • %e:科学计数形式,即数值表示成 a×10b 形式; • %f:固定小数点位置的数据形式; • %g:在上述两种格式中自动选取较短的格式。 可以用一些特殊格式比如\n、\r、\t、\b、\f 等来产生换行、回车、tab、退格、走纸等字 符,用\\来产生反斜线符号\,用%%来产生百分号。此外还可以包括数据占用的最小宽度和数 据精度的说明。举例说明此命令的用法: %将一个平方根表写入 s_table.dat a=1:10; b=[a;sqrt(a)]; –354– 第 9 章 文件 I/O fid=fopen(’s_table.dat’,’w’); fprintf(fid,’table of square root:\n’); fprintf(fid,’%2.0f %6.4f\n’,b); fclose(fid); 运行后将 s_table.dat 打印出来,可得如下代码: >> type s_table.dat table of square root: 1 1.0000 2 1.4142 3 1.7321 4 2.0000 5 2.2361 6 2.4495 7 2.6458 8 2.8284 9 3.0000 10 3.1623 在本例中,第一条 fprintf 语句输出一行标题,随后换行,第二条 fprintf 语句输出函数值 表,每组自变量和函数占一行,都是固定小数点位置的形式。自变量值占 2 个字符位,不带 小数;函数值占 6 个字符位,小数点后的精度是 4 个字符位,自变量和函数值之间空两格。 矩阵 y 的元素按列的顺序转换成格式化的输出,函数反复使用格式说明,直至将矩阵的 数据转换完毕。 注意:sprintf 函数与 fprintf 函数功能类似,但是 sprintf 将数据以字符串形式返回,而不是直接写 入文件。 9.6 文件内的位置控制 根据操作系统的规定,在读写数据时,缺省的方式总是从磁盘文件的开始顺序向后的在 磁盘空间上读写数据。操作系统通过一个文件指针,来指示当前的文件位置。C 或 Fortran 语 言都有专门的函数来控制和移动文件指针,达到随机访问磁盘文件的目的。MATLAB 中也有 类似的函数,如表 9-3 所示。 表 9-3 feof fseek ftell frewind 函数 控制文件内位置指针的函数 功能 测试指针是否在文件结束位置 设定文件指针位置 获取文件指针位置 重设指针至文件起始位置 –355– MATLAB 7.0 • feof feof 用于测试指针是否在文件结束位置,其语法格式为: feof(fid) 如果文件标识为 fid 的文件的末尾指示值被置位,则此命令返回 1,说明指针在文件末尾, 否则返回 0。 • fseek fseek 用于设定指针位置,其语法格式为: status=fseek(fid,offset,origin) 其中 fid 是文件标识;offset 是偏移量,以字节为单位,可以是整数(表示要往文件末尾 方向移动指针)、0(不移动指针位置)或负数(表示往文件起始方向移动指针);origin 是基 准点,可以是’bof’(文件的起始位置)、’cof’(指针的目前位置)、’eof’(文件的末尾),也可以 用-1、0 或 1 来表示。 如果返回值 status 为 0 表示操作成功,返回-1 表示操作失败,如果要了解更多信息可以 调用 ferror 函数。 • ftell ftell 用于返回现在的位置指针,其语法格式为: position=ftell(fid) 返回值 position 是距离文件起始位置的字节数,如果返回-1 则说明操作失败。 • frewind frewind 用于将指针返回到文件开始,语法格式为: frewind(fid) 下面通过一个例子来介绍这几个命令的使用方法,示例代码如下: a=[1:6]; fid=fopen(’six.bin’,’w’); fwrite(fid,a,’short’); status=fclose(fid); fid=fopen(’six.bin’,’r’); six=fread(fid,’short’); eof=feof(fid); frewind(fid); status=fseek(fid,2,0); position=ftell(fid); 运行后,检查 six、eof、status 和 position 的值: >> six’ ans = 123456 >> eof eof = 1 >> status –356– status = 0 >> position position = 2 第 9 章 文件 I/O –357– 第 10 章 信号处理工具箱 MATLAB 的推出得到了各个领域专家学者的关注,其强大的扩展功能为各个领域的应用 提供了基础,相继推出了各种 MATLAB 工具箱,为各个层次的研究人员提供了有力的工具, 节省了时间。本章将介绍信号处理(signal processing)工具箱的使用。 10.1 数字信号处理基本理论 在计算机中,所有的信号都是离散信号,因此在使用 MATLAB 进行信号处理之前,首 先要了解离散时间信号处理的相关理论。 10.1.1 离散信号与系统 1.离散信号 信号是信息的表现形式,是通信传输的客观对象,其特性可以从两个方面来描述,即时 间特性和频率特性。信号之所以不同是因为各自有不同的时间特性和频率特性,并且两者之 间有一定的对应关系。若 t 是定义在时间轴上的离散点,则 x(t)为离散时间信号,表示为 x(nT), T 表示相邻两个点之间的时间间隔,也称为抽样周期,n 取整数并代表时间的离散时刻。一 般可以把 T 归一化为 1,这样 x(nT)可简记为 x(n),又称为离散时间序列。 在 MATLAB 中,可用一个向量来表示一个有限长度的序列(由于内存的限制,不可能 表示一个任意无限序列),示例代码设置如下: x(n)=[4,5,2,3,9,8,0,7,-1] MATLAB 对下标的约定从 1 开始递增,如果要包含采样时刻的信息,则需要两个向量来 表示,对于上面的序列应有: n=[-2,-1,0,1,2,3,4,5,6] x=[4,5,2,3,9,8,0,7,-1] 为了分析的方便,数字信号处理理论中定义了一些基本的典型的序列,其定义和 MATLAB 表达式如下: • 单位取样序列 δ (n) = 1 0 n =0 n ≠0 该表达式可利用 zeros 函数或者逻辑关系来实现,代码设置: x=zeros(1,N); x(1)=1; 或者可以写成: n=1:N x=[n==1]; • 单位阶跃序列 u (n) = 1 0 n≥0 n<0 该表达式可利用 ones 函数来实现,代码设置如下: x=ones(1,N) • 实指数序列 第 10 章 信号处理工具箱 x(n) = a n ∀n ; a ∈ R 该表达式通过 MATLAB 实现,代码设置如下: n=0:N-1; x=a.^n; • 复指数序列 x(n) = e(σ + jω0 )n ∀n 该表达式通过 MATLAB 实现,代码设置如下: n=0:N-1; x=exp(a+j*w0)*n); • 正(余)弦序列 x(n) = cos(ω0n + θ ) ∀n 该表达式通过 MATLAB 实现,代码设置如下: n=0:N-1; x=cos(w0*n+q); • 随机序列 在 MATLAB 中提供了两类(伪)随机信号: rand(1,N)产生[0,1]上均匀分布的随机矢量;randn(1,N)产生均值为 0,方差为 1 的高斯随 机序列。其他分布的随机数可通过上述随机数的变换而产生。 • 周期序列 x(n)=x(n+N) ∀n 2.离散系统 系统是由若干相互作用和相互依赖的事物组合而成的并具有特定功能的整体。系统分析 的着眼点是分析系统的输入和输出的关系,而不涉及系统的内部情况。 一个离散系统可以抽象为一种变换或者一种映射,即 y(n)=T[x(n)]。其中 T 代表变换。在 数字信号处理中,所研究的系统基本上都是线性时不变系统(LSI 系统),其输入/输出关系 可以通过冲激响应 h(n)表示: –359– MATLAB 7.0 从入门到精通 ∞ y(n) = x(n) ⊗ h(n) = ∑ x(k)h(n − k) k =−∞ 其中 x(n)和 h(n)作卷积运算,MATLAB 提供了求卷积函数 conv。有关线性时不变系统中 涉及的重要概念,比如稳定性、因果性和移不变性等可参看信号与系统的经典教材。 10.1.2 Z 变换 Z 变换是离散系统和离散信号分析、综合的重要工具,其地位和作用如同拉普拉斯变换 之于连续信号和系统。 1.Z 变换的定义 给定离散信号 x(n),其 Z 变换的定义为: ∑ X (z) = x(n)z −n n 其中 z 为复变量。如果 n 的取值范围为 − ∞ ~ +∞ ,则上式定义的 Z 变换称为双边 Z 变 换,如果 n 的取值范围为 0 ~ +∞ ,则上式称为单边 Z 变换。实际的物理系统的抽样响应 h(n) 在 n<0 时恒为 0,因此对应的都是单边 Z 变换。 X(z)存在的 z 的集合称为收敛域(ROC),即满足下式: ∑ X (z) = x(n)z −n < ∞ n ROC 由|z|决定,因此一般为环形。序列在单位圆上的 Z 变换就是序列的傅立叶变换,若 x(n)=h(n),则该变换就是系统的频率响应。 2.Z 变换的性质 • 线性 若 Z[x1(n)] = X1 (z) , ROC : R1 ; Z[x2 (n)] = X 2 (z) , ROC : R2 。则有: Z[ax1(n) + bx2 (n)] = aX1 (z) + bX 2 (z) ,ROC 为 R1 和 R2 的公共部分。 • 时移性质 Z[x(n − k )] = z −k X (z) ,ROC 不变。 • 频移性质 Z[a n x(n)] = X (z), a ROC : ROCx / | a | • 折叠 Z[x(−n)] = X (1) z , ROC : 与ROCx相反,即ROCx的逆 。 • 复共轭 –360– 第 10 章 信号处理工具箱 Z[x* (n)] = X * (z* ) , ROC : ROCx • Z 域微分 Z[nx(n)] = −z dX (z) dz , ROC : ROCx • 序列相乘 ∫ Z[x1(n)x2 (n)] = 1 2πj c X1(v)X 2 (z v )v −1dv ,ROC : ROCx1 ∩ ROCx2的逆 ,其中 C 为公 共 ROC 中包围原点的闭合围线。 • 序列卷积 Z[x1 (n) ⊗ x2 (n)] = X1 (z) X 2 (z) , ROC : ROCx1 ∩ ROCx21 10.1.3 离散傅立叶变换 Z 变换提供了任意序列在频域的表示方法,但它是连续变量 z 的函数,因此无法直接利 用计算机进行数值计算。为了使用 MATLAB,必须截断序列,得到有限个点的表达式。这就 产生了离散傅立叶级数(DFS)、离散傅立叶变换(DFT)和计算量小的快速傅立叶变换(FFT)。 如果信号在频域上是离散的,则该信号在时域上就是周期性的函数。反之在时域上离散 的信号在频域上必然表现为周期性的频率函数。可以得出一个一般规律:一个域的离散必然 造成另一个域的周期延拓。这种离散变换,本质上都是周期的。因此,首先从周期序列及其 傅立叶级数开始讨论,然后再讨论可作为周期序列一个周期的、有限长序列的离散傅立叶变 换(DFT)。 1.离散傅立叶级数(DFS) ~ ~ 对于周期为 N 的离散时间信号序列 x(n) = x(n + kN ) ,其中 k 为任意整数,由于在 Z 平 面上没有任何收敛区域,所以不能进行 Z 变换,但是可以用傅立叶级数来表达,其基波频率 j 2π n j 2π kn 为 2π / N , 用 复 指 数 表 示 为 e1 (n) = e N , 第 k 次 谐 波 为 ek (n) = e N , 所 以 有 ek+N (n) = ek (n) ,可得离散傅立叶级数公式如下: ∑ ~ x(n) = 1 N −1 ~ j 2π kn X (k)e N N k=0 ~ 在上式中求和号前所乘的系数 1/N 是习惯上采用的常数, X (k) 是 k 次谐波的系数: –361– MATLAB 7.0 从入门到精通 ∑ ~ N −1 ~ − j 2π kn X (k) = x(n)e N n=0 习惯上也常采用符号WN = e− j(2π / N ) ,这样离散傅立叶级数对可以表示为:  ∑    ∑  ~ x(n) = 1 N N −1 ~ X (k )WN k =0 ~ N −1 ~ X (k) = x(n)WN kn n=0 − kn 2.离散傅立叶变换(DFT) 周期序列实际上只有有限个序列值有意义,因此它的许多特性可以沿用到有限长序列 上,对于一个长度为 N 的有限长序列 x(n),以 x(n)为主值序列并以 N 为周期进行延拓得到周 ~ ~ ∞ ~ ∑ 期序列 x(n) ,即 x(n) = x(n + rN ) ,当 0≤n≤N-1 时, x(n) = x(n) ,n 为其余值时 x(n) r =−∞ 为 0。 由离散傅立叶级数公式,可以得到有限长序列 x(n)的离散傅立叶变换公式: ∑ x(n) = 1 N N −1 X (k )WN −kn k=0 0≤n≤N-1 N −1 ∑ X (k) = x(n)WN kn n=0 0≤k≤N-1 离散傅立叶级数和离散傅立叶变换与 Z 变换有类似的性质,详细的介绍可以参照信号与 系统教材,本节不再赘述。 3.快速傅立叶变换(FFT) 快速傅立叶变换是离散傅立叶变换的快速算法,其应用极大地推动了 DSP 理论和技术的 发展。下面介绍 FFT 的基本思想和实现。 • FFT 的基本思想 N 点序列的 DFT 为 N −1 ∑ X (k ) = x(n)WN kn n=0 0≤k≤N-1 由 于 系 数 WN kn 是 一 个 周 期 函 数 : WN n( N −k ) = WN k (N −n) = WN −nk , 且 是 对 称 的 : W nk+ N / 2 N = −WN nk 。 –362– 第 10 章 信号处理工具箱 FFT 正是基于这样的基本思想而发展起来的。 • FFT 的实现方法 FFT 的算法形式有很多种,但基本上可以分为两大类:时间抽取法(DIT-FFT)和频率 抽取法(DIF-FFT)。这两种算法在思想上基本一致,只是划分方式略有差异,这里仅以 DIT-FFT 为例进行说明。 当 N 是 2 的整数次方时,称为基 2 的 FFT 算法。 首先将序列 x(n)分解为两组,偶数项为一组,奇数项为一组:    x(2r x(2r ) = x1 (r) + 1) = x2 (r ) r=0,1,…,N/2-1 将两组分别进行 N/2 点的 DFT 可得:   X X (k) (N = /2 X + 1 (k k) ) + WN k X 2 (k) = X1(k) −WN k X 2 (k ) k=0,1,…,N/2-1 重复这一过程,可得到 x(n)的 FFT,整个运算需要 N 2 log 2 N 次复数乘法和 N log2 N 次 复数加法。 在 MATLAB 中,可直接利用内部函数 fft 进行计算,速度比较快,有关其具体用法在本 章的第 3 节会给出例子。 10.1.4 数字滤波器结构 数字信号处理的目的之一是设计某种设备或建立某种算法用以处理序列,使序列具有某 种确定的性质,这种设备或算法结构就称为数字滤波器。数字滤波器可以分为有限冲激响应 (FIR)和无限冲激响应(IIR)两种。滤波器的设计结果受滤波器的类型和结构的影响。本节 分别介绍这两种滤波器的结构。 1.IIR 滤波器 IIR 滤波器的系统函数为: M ∑ br z −r H (z) = r=0 N ∑ 1 + ak z −k k =1 其差分方程可表示为: M N y(n) = ∑ br x(n − r) −∑ ak y(n − k) r=0 K =1 由其系统函数或差分方程可知,实现 IIR 滤波器有直接型、级联型和并联型三种结构。 –363– MATLAB 7.0 从入门到精通 • 直接型 直接型分为直接Ⅰ型和直接Ⅱ型两种,直接Ⅰ型可由差分方程得到,第一部分 M ∑ br x(n − r) 表示将输入信号进行延时,组成 M 节的延时网络,把每节延时抽头与常系数 r=0 相乘,再把结果相加,这是一个横向结构网络,即实现零点的网络,第二部分表示将信号输 出延时,组成 N 节的延时网络,把每节延时抽头后与常系数相乘,然后再把结果相加,由于 这部分是对输出的延时,故为反馈网络,这部分网络实现极点。y(n)的信号流图如图 10-1 所 示。 b0 z −1 b1 z −1 b2 − a1 z −1 − a2 z −1 z −1 bM − aN z −1 图 10-1 直接Ⅰ型 直接Ⅱ型可以由系统函数得到,将分子和分母的倒数分别看作一个网络,则整个系统可 看作是这两个网络的级联,如图 10-2 所示。 − a1 − a2 b0 z −1 b1 z −1 b2 − aN bM z −1 图 10-2 直接Ⅱ型 直接Ⅱ型结构比直接Ⅰ型速度快且经济(延时单元少一半),但它们的系数都不能直接 控制滤波器特性,对字长效应敏感。 在 MATLAB 中,可以 DSP 工具函数 filter 实现 IIR 的直接形式。 • 级联型 对 IIR 滤波器系统函数进行因式分解,若 ak , br 均为实数,则零点和极点只有实数和共 轭复数,合并共轭因子有如下的表达式: –364– M1 M2 ∏ ∏ A (1 − gi z −1 ) (1 + β1i z −1 + β 2i z −2 ) H(z) = i =1 N1 i =1 N2 ∏ ∏ (1 − pi z −1 ) (1 + α1i z −1 + α 2i z −2 ) i=1 i=1 将单实根视为二阶因子的特例,设 M≤N,则: ∏ ∏ H (z) = L A i=1 (1 + β1i z −1 (1 + α1i z −1 + β 2i z −2 ) + α 2i z −2 ) = A L i=1 Hi (z) 第 10 章 信号处理工具箱 其中,L 表示(N+1)/2 中的最大整数。 H i (z) 称为二阶基本节,可以采用直接Ⅱ型实现, H(z)则由 H i (z) 级联而成,级联型的具体体现形式如图 10-3 所示。 H1(z) H L (z) α1i z −1 β1i α 2i z −1 β 2i 图 10-3 级联型 在 MATLAB 中,给定直接形式滤波器的系数,可计算出级联形式的系数,这可由扩展 函数 dir2cas 实现,然后利用扩展函数 casfiltr 实现滤波器的级联形式。 • 并联型 作为系统函数的另一种表示形式,可以将 H(z)表示如下形式的部分分式展开: ∑ ∑ H (z) = M −N Gk z −k k =0 + K β 0k + β1k z −1 k =1 1 + α1k z −1 + α 2k z −2 其中 K=N/2,每一部分均为二阶,它们之间是并联关系,M=N=4 为例,画出其结构图 如图 10-4 所示。 G0 α 11 α 21 α 12 α 22 β 01 z −1 β11 z −1 β 02 z −1 β12 z −1 图 10-4 并联型 –365– MATLAB 7.0 从入门到精通 在 MATLAB 下,已经设计了将滤波器直接形式转化为并联形式的扩展函数 dir2par,其 中用到了复共轭对比较函数 cplxcomp,这样就可利用 parfiltr 函数实现滤波器的并联形式。 2.FIR 滤波器 有限冲激响应(FIR)滤波器的系统函数为: H (z) = b0 + b1 z −1 + ...+ b z −(M −1) M −1 差分方程表示为: y(n) = b0 x(n) + b1x(n −1) + ... + bM −1x(n − M + 1) 与 IIR 滤波器相比,FIR 滤波器结构相对简单,而且可以设计成线性相位。实现 FIR 滤 波器可采用直接形式、级联形式、线性相位形式和频率取样形式四种结构。 • 直接形式 与 IIR 类似,以 M=5 为例,结构如图 10-5 所示,在 MATLAB 中可通过 filter 函数实现。 b0 z −1 β 01 z −1 b1 z −1 b2 z −1 b3 b4 • 级联形式 图 10-5 FIR 滤波器的直接形式 K ∏ H (z) = b0 + b1 z −1 + ... + bM −1 z −(M −1) = b0 (1 + β k1 z −1 + β k 2 z −2 ) k =1 其中,K=M/2,以 M=7 为例,其结构如图 10-6 所示。 b0 z −1 β11 z −1 β12 z −1 β 21 z −1 β22 z −1 β31 z −1 β32 图 10-6 FIR 滤波器级联形式 在 MATLAB 中,可采用 dir2cas 和 casfiltr 来实现。 • 线性相位形式 线性相位条件 ∠H (e jω ) = β − αω , − π < ω ≤π ,其中 β = 0或 ± π / 2 ,α 为常数。 对 H(z)则有下列对称性: h(n) = h(M −1 − n) β = 0 , 0≤n≤M-1 (对称冲激响应) –366– 第 10 章 信号处理工具箱 h(n) = −h(M −1 − n) β = ± π ,0≤n≤M-1 2 (反对称冲激响应) 以 M 为 7(代表奇数)和 M 为 6(代表偶数)为例,对称型的结构如图 10-7 所示。 z −1 z −1 z −1 z −1 z −1 z −1 b0 z −1 b1 z −1 b2 b3 z −1 b0 z −1 b1 z −1 b2 图 10-7 FIR 滤波器线性相位形式 在 MATLAB 中,实现 FIR 线性相位形式的函数等同于直接形式,可用 filter 函数。 • 频率取样形式 冲激响应 h(n)的 M 点 DFT 为 H(k)(0≤k≤M-1),则有 H(z)=Z[h(n)]=Z[IDFT(H(k))] 利用内插公式可得: ∑ H (z) =  1 − z− M M Mk =−011 − H (k WM − ) k z −1 这表明在这种结构中采用了离散傅立叶变换 H(k),而不是冲激响应 h(n)。这种结构分成 两部分,以 M 取 4 为例,频率取样形式如图 10-8 所示。 W40 z −1 − z −4 W41 z −1 W4 2 z −1 W43 z −1 图 10-8 FIR 滤波器频率取样形式 在 MATLAB 中,给定 h(n)或 H(k),则可借助于扩展函数 dir2fs 得到频率取样形式,它将 h(n)值转换为频率取样形式。 此外还有一种广泛应用的格形滤波器结构,可参阅有关文献。在第 3 节中将具体讨论这 些滤波器基于 MATLAB 的实现方法。 –367– MATLAB 7.0 从入门到精通 10.2 MATLAB 7.0 的信号处理工具箱函数 MATLAB 包含了进行信号处理的许多工具箱函数,有关这些工具箱函数的使用可通过 Help 命令得到。为使用方便,本节将按分组给出这些函数的简单说明,在后面的章节会给出 其中一部分函数的使用举例。详细的文档可以在 Help 目录下找到。 10.2.1 波形产生(Waveform Generation) 在该工具箱里包含以下几个函数: • chirp:产生扫频余弦函数; • diric:产生 Dirichlet 或周期 sinc 函数; • gauspuls:产生高斯调制的正弦曲线脉冲; • gmonopuls:产生高斯单脉冲; • pulstran:产生一个脉冲序列; • rectpuls:产生一个非周期的抽样方波; • sawtooth:产生锯齿波或三角波; • sinc:产生 sinc 函数,即 sin(πt) ; πt • square:产生方波; • tripuls:产生一个非周期的采样三角波; • vco:压控振荡器。 10.2.2 滤波器分析(Filter Analysis) 在该工具箱里包含以下几个函数: • abs:求绝对值(幅值,这是一个 MATLAB 函数); • angle:求相角(这是一个 MATLAB 函数); • freqs:模拟滤波器的频率响应; • freqspace:频率响应中的频率间隔(这是一个 MATLAB 函数); • freqz:计算数字滤波器的频率响应; • fvtool:打开滤波器可视化工具; • grpdelay:计算平均滤波器延迟(群延迟); • impz:计算数字滤波器的冲激响应; • phasedelay:计算数字滤波器的相位延迟响应; • phasez:计算数字滤波器的相位响应; • stepz:计算数字滤波器的阶跃响应; • unwrap:展开相角(这是一个 MATLAB 函数); • zerophase:计算数字滤波器的零相位响应; • zplane:离散系统零极点图。 –368– 10.2.3 滤波器实现(Filter Implementation) 第 10 章 信号处理工具箱 在该工具箱里包含以下几个函数: • conv:求卷积和多项式乘法(这是一个 MATLAB 函数); • conv2:二维卷积(这是一个 MATLAB 函数); • convmtx:卷积矩阵; • deconv:反卷积和多项式除法(这是一个 MATLAB 函数); • fftfilt:采用重叠相加法基于 FFT 的 FIR 滤波器实现; • filter:直接滤波器实现(这是一个 MATLAB 函数); • filter2:二维数字滤波(这是一个 MATLAB 函数); • filtfilt:零相位数字滤波; • filtic:直接 II 型滤波器的初始条件选择; • latcfilt:格型和格-梯型滤波器实现; • medfilt1:一维中值滤波; • sgolayfilt:Savitzky-Golay 滤波; • sosfilt:二阶(四次)IIR 数字滤波; • upfirdn:过采样,FIR 滤波和抽样。 10.2.4 线性系统变换(Linear System Transformations) 在该工具箱里包含以下几个函数: • latc2tf:将格形滤波器参数转换维传输函数格式; • polystab:稳定多项式; • polyscale:多项式的根的数值范围; • residuez:Z 变换部分分式展开或留数计算; • sos2ss:变系统二阶分割形式为状态空间形式; • sos2tf:变系统二阶分割形式为传递函数形式; • sos2zp:变系统二阶分割形式为零极点增益形式; • ss2sos:变系统状态空间形式为二阶分割形式; • ss2tf:变系统状态空间形式为传递函数形式; • ss2zp:变系统状态空间形式为零极点增益形式; • tf2latc:变传递参数形式为格形滤波器形式; • tf2sos:变传递函数形式为系统二阶分割形式; • tf2ss:变传递函数形式为系统状态空间形式; • tf2zp:变连续时间传递函数为零极点增益形式; • tf2zpk:变离散时间传递函数为零极点增益形式; • zp2sos:变零极点增益形式为二阶分割形式; • zp2ss:变零极点增益形式为状态空间形式; • zp2tf:变零极点增益形式为传递函数形式。 –369– MATLAB 7.0 从入门到精通 10.2.5 FIR 滤波器设计(FIR Digital Filter Design) 在该工具箱里包含以下几个函数: • cfirpm:复杂非线性相位等纹波滤波器设计; • dfilt:用面向对象的方式产生滤波器; • fir1:基于窗函数的 FIR 滤波器设计; • fir2:基于频率取样的 FIR 滤波器设计; • fircls:多波段有限最小二乘 FIR 滤波器设计; • fircls1:低通和高通线性相位 FIR 滤波器的有限最小二乘设计; • firgauss:高斯 FIR 滤波器设计; • firls:最小二乘线性相位 FIR 滤波器设计; • firpm:Parks-McClellan 最优化 FIR 滤波器设计; • firpmord:Parks-McClellan 最优化 FIR 滤波器阶估计; • firrcos:升余弦 FIR 滤波器设计; • intfilt:内插 FIR 滤波器设计; • kaiserord:用 Kaiser 窗进行设计的 FIR 滤波器的参数估计; • sgolay:Savitzky-Golay 滤波器设计。 10.2.6 IIR 滤波器设计(IIR Digital Filter Design) 在该工具箱里包含以下几个函数: • butter:Butterworth(巴特沃思)模拟和数字滤波器设计; • cheby1:Chebyshev(切比雪夫)I 型滤波器设计; • cheby2:Chebyshev II 型滤波器设计; • dfilt:用面向对象的方法产生滤波器; • ellip:椭圆滤波器设计; • filtstates:包含滤波器状态信息的对象; • maxflat:归一化数字 Butterworth 滤波器设计; • yulewalk:递归数字滤波器设计。 10.2.7 IIR 滤波器阶的选择(IIR Filter Order Estimation) 在该工具箱里包含以下几个函数: • buttord:计算 Butterworth 滤波器的阶和截止频率; • cheb1ord:计算 Chebyshev I 型滤波器的阶; • cheb2ord:计算 Chebyshev II 型滤波器的阶; • ellipord:计算椭圆滤波器的最小阶。 –370– 10.2.8 变换(Transforms) 在该工具箱里包含以下几个函数: • bitrevorder:将输入序列按比特反向变换; • czt:线性调频 Z 变换; • dct:离散余弦变换(DCT); • dftmtx:离散傅立叶变换矩阵; • digitrevorder:将输入序列按数字反向变换; • fft:一维快速傅立叶变换; • fft2:二维快速傅立叶变换; • fftshift:重新编排 FFT 函数的输出; • goertzel:用二阶 Goertzel 算法计算离散傅立叶变换; • hilbert:希尔伯特变换; • idct:逆离散余弦变换; • ifft:一维逆快速傅立叶变换; • ifft2:二维逆快速傅立叶变。 第 10 章 信号处理工具箱 10.2.9 统 计 信 号 处 理 和 谱 分 析 ( Statistical Signal Processing and Spectral Analysis) 在该工具箱里包含以下几个函数: • corrcoef:计算相关系数矩阵; • corrmtx:计算自相关矩阵的数据矩阵; • cov:协方差矩阵; • cpsd:两个信号的互谱密度估计; • dspdata:DSP 数据对象的参数信息; • dspopts:频谱对象的可选参数信息; • mscohere:两个信号之间的幅度自相关函数估计; • pburg:基于 Burg 方法的功率谱密度估计; • pcov:基于协方差方法的功率谱密度估计; • peig:基于特征向量方法的伪谱; • periodogram:基于周期图的功率谱密度估计; • pmcov:基于修正协方差方法的功率谱密度估计; • pmtm:基于 MTM 方法的功率谱密度估计; • pmusic:基于 MUSIC 算法的功率谱密度估计; • pwelch:基于 Welch 方法的功率谱密度估计; • pyulear:基于 Yule-Walker AR 方法的功率谱密度估计; • rooteig:基于特征向量方法的频率和功率分析; • rootmusic:基于 root MUSIC 算法的频率和功率分析; • spectrum:含有频谱估计方法的参数信息的对象; –371– MATLAB 7.0 从入门到精通 • tfestimate:从输入和输出估计传递函数; • xcorr:互相关函数估计; • xcorr2:二维互相关函数估计; • xcov:互协方差函数估计。 10.2.10 窗函数(Windows) 在该工具箱里包含以下几个函数: • barthannwin:修正的 Bartlett-Hann 窗; • bartlett:Bartlett(巴特利特)窗; • blackman:Blackman(布莱克曼)窗; • blackmanharris:最小化 4 阶 Blackman-Harris 窗; • bohmanwin:Bohman 窗; • chebwin:Chebyshev 窗; • flattopwin:平坦顶部窗; • gausswin:Gaussian(高斯)窗; • hamming:Hamming(汉明)窗; • hann:Hann(汉宁)窗; • kaiser:Kaiser(凯泽)窗; • nuttallwin:Nuttall 定义的最小化 4 阶 Blackman-Harris 窗; • parzenwin:Parzen 窗; • rectwin:矩形窗; • sigwin:用面向对象方法生成窗; • triang:三角窗; • tukeywin:Tukey 窗; • window:窗函数生成; • wvtool:窗可视化工具。 10.2.11 参数化建模(Parametric Modeling) 在该工具箱里包含以下几个函数: • arburg:基于 Burg 方法的 AR 模型参数估算; • arcov:基于协方差方法的 AR 模型参数估算; • armcov:基于修正协方差方法的 AR 模型参数估算; • aryule:基于 Yule-Walker 方法的 AR 模型参数估算; • ident:查看系统识别工具箱文件; • invfreqs:模拟滤波器拟合频率响应; • invfreqz:离散滤波器拟合频率响应; • prony:利用 Prony 法的离散滤波器拟合时间响应; • stmcb:利用 Steiglitz-McBride 迭代方法求线性模型。 –372– 10.2.12 特殊操作(Specialized Operations) 在该工具箱里包含以下几个函数: • buffer:将信号向量缓存在数据帧矩阵中; • cell2sos:将二阶分区的单元序列转换为二阶分区矩阵; • cplxpair:将复数归成复共轭对; • demod:通信仿真中的解调; • dpss:离散椭球体序列(Slepian 序列); • dpssclear:清除数据库中的 Slepian 序列; • dpssdir:Slepian 序列的数据库目录; • dpssload:从数据库中加载 Slepian 序列; • dpsssave:保存 Slepian 序列; • eqtflength:使传输函数分子和分母等长; • modulate:通信仿真中的调制; • seqperiod:计算序列周期; • sos2cell:将二阶分区矩阵转换为单元序列; • specgram:频谱分析; • stem:离散数据序列作图; • strips:条状图; • udecode:将 2n 进制整型输入解码为浮点数输出; • uencode:将浮点数输入编码为整型输出。 第 10 章 信号处理工具箱 10.2.13 模拟低通滤波器原型(Analog Lowpass Filter Prototypes) 在该工具箱里包含以下几个函数: • besselap:Bessel 模拟低通滤波器原型; • buttap:Butterworth 模拟低通滤波器原型; • cheb1ap:Chebyshev I 型模拟低通滤波器原型; • cheb2ap:Chebyshev II 型模拟低通滤波器原型; • ellipap:椭圆模拟低通滤波器原型。 10.2.14 模拟滤波器设计(Analog Filter Design) 在该工具箱里包含以下几个函数: • besself:Bessel 模拟滤波器设计; • butter:Butterworth 模拟数字滤波器设计; • cheby1:Chebyshev I 型滤波器设计; • cheby2:Chebyshev II 型滤波器设计; • ellip:椭圆滤波器设计。 –373– MATLAB 7.0 从入门到精通 10.2.15 模拟滤波器转换(Analog Filter Transformation) 在该工具箱里包含以下几个函数: • lp2bp:将低通模拟滤波器转换为带通滤波器; • lp2bs:将低通模拟滤波器转换为带阻滤波器; • lp2hp:将低通模拟滤波器转换为高通滤波器; • lp2lp:改变模拟低通滤波器的截止频率。 10.2.16 滤波器离散化(Filter Discretization) 在该工具箱里包含以下几个函数: • bilinear:双线性变换法实现模拟到数字的滤波器变换; • impinvar:脉冲响应不变法实现模拟到数字的滤波器变换。 10.2.17 对数倒谱分析(Cepstral Analysis) 在该工具箱里包含以下几个函数: • cceps:倒谱分析; • icceps:逆倒谱分析; • rceps:实倒谱和最小相位重构。 10.2.18 线性预测(Linear Prediction) 在该工具箱里包含以下几个函数: • ac2poly:将自相关序列转换为预测多项式; • ac2rc:将自相关序列转换为反射系数; • is2rc:将反正弦参数转换为反射系数; • lar2rc:将对数域比例参数转换为反射系数; • levinson:Levinson-Durbin 递归算法; • lpc:计算线性预测系数; • lsf2poly:将线性谱频率转换为预测系数; • poly2ac:将预测多项式转换为自相关序列; • poly2lsf:将预测系数转换为线性谱频率; • poly2rc:将预测多项式转换为反射系数; • rc2ac:将反射系数转换为自相关序列; • rc2is:将反射系数转换为反正弦参数; • rc2lar:将反射系数转换为对数域比例参数; • rc2poly:将反射系数转换为预测多项式; • rlevinson:逆 Levinson-Durbin 递归; • schurrc:利用自相关序列计算反射系数。 –374– 第 10 章 信号处理工具箱 10.2.19 多速信号处理(Multirate Signal Processing) 在该工具箱里包含以下几个函数: • decimate:降低序列的采样速率; • downsample:采样速率整数倍下降; • interp:提高采样速率; • interp1:一维数据插值; • resample:按有理数因数改变采样率; • spline:三次样条函数内插; • upfirdn:过采样,FIR 滤波,取样; • upsample:采样速率整数倍提高。 10.2.20 图形用户接口(Graphical User Interfaces) 在该工具箱里包含以下几个函数: • fdatool:打开滤波器设计和分析工具; • fvtool:打开滤波器可视化工具; • sptool:交互式数字信号处理工具(SP 工具); • wintool:打开窗函数设计和分析工具; • wvtool:打开可视窗工具。 10.3 基于 MATLAB 的信号处理系统分析与设计 本节主要介绍 MATLAB 在信号处理领域一些常用的实例,是对前两节基础知识的综合 运用。 10.3.1 离散信号与系统的 MATLAB 实现 【例 10.1】离散信号的运算是实现各种信号和系统的基础,本例给出信号基本运算的 MATLAB 实现,包括位移、相加、相乘以及变换等。 给 定 离 散 信 号 为 x1(n) 和 x2 (n) , 信 号 y1(n) = x1(n − k) , y2 (n) = x1(n + k) , x(n) = x1(n) + x2 (n) , y(n) = x1(n)x2 (n) ,编写程序实现上述运算,并且计算 x(n) 的功率 和能量,对 x(n) 进行信号折叠和奇偶分解。 MATLAB 程序代码如下: function [yshift,n]=sig_shift(x,m,n0) %信号延迟的程序,m 为输入 x 的下标 –375– MATLAB 7.0 从入门到精通 %n0 为延迟的单位长度 n=m+n0; yshift=x; function [yam,n]=sig_proc(x1,n1,x2,n2,s) %信号相加和相乘的程序 %x1,x2 分别为输入序列,n1,n2 为对应下标 %s=0 为加法;其他值为乘法 n=min(min(n1),min(n2)):max(max(n1),max(n2)); y1=zeros(1,length(n)); y2=y1; y1(find((n>=min(n1))&(n<=max(n1))==1))=x1; y2(find((n>=min(n2))&(n<=max(n2))==1))=x2; if s==0 y==y1+y2; else y=y1.*y2; end function [yengy,ypower,yop]=sig_energy(x,n) %计算信号 x 的能量和功率,并进行信号折叠 %n 为信号 x 的下标 yengy=sum(abs(x).^2); ypower=yengy/length(x); yop=fliplr(x); n=-fliplr(n); function [x_even,x_odd]=sig_evenodd(x,n) %信号奇偶分解的程序 %x 为输入序列,n 为对应下标 m=fliplr(n); mm=min([m,n]):max([m,n]); nm=n(1)-m(1); nl=1:length(n); xl=zeros(1,length(mm)); xl(nl+nm)=x; x=xl; x_even=0.5*(x+fliplr(x)); x_odd=0.5*(x-fliplr(x)); 【例 10.2】用 MATLAB 编写生成均值为 a,方差为 b 的高斯随机序列信号。 –376– 第 10 章 信号处理工具箱 MATLAB 程序代码如下: function y=randn_ab(N,a,b) %产生均值为 a 方差为 b 的 N 点高斯随机序列 %N 为随机序列的长度 x=randn(1,N); y=a+sqrt(b)*x; plot(y); grid; 以上是离散信号的产生例子,在实际操作中可以灵活运用 MATLAB 提供的各种运算和 工具箱函数实现系统需要的信号形式。 分析一个离散系统最重要的是得到其系统单位抽样响应,用 filter 函数或者 impz 函数即 可实现。 【例 10.3】求如图 10-9 所示的离散系统的单位抽样响应。 z −1 z −1 图 10-9 离散系统的信号流图 该离散系统对应的输入输出差分方程为: y(n) - 0.4y(n -1) - 0.5y(n - 2) = 0.2x(n) + 0.1x(n -1) MATLAB 程序代码如下: pul=[1 zeros(1,63)];%产生单位取样序列 b=[0.2 0.1]; a=[1 -0.4 -0.5];%b 和 a 都是系统参数 h=filter(b,a,pul); hl=impz(b,a,64); figure(1) %作图 stem(h) title('FILTER function') figure(2) stem(hl) title('IMPZ function') 程序采用 MATLAB 给出的两种函数 filter 和 impz 来计算,得到的系统单位响应曲线如 图 10-10 所示,从图中可以看出两函数求得的结果相同。 –377– MATLAB 7.0 从入门到精通 图 10-10 单位抽样响应 在进行离散系统的分析时,系统的频率响应和零极点增益也是常常需要考虑的问题,可 分别用 freqz 函数和 roots 函数来实现。 10.3.2 离散傅立叶变换的 MATLAB 实现 周期序列实际上只有有限个序列值有意义,因此它的许多特性可以沿用到有限长序列 上,由离散傅立叶级数的公式可以得到有限长序列的离散傅立叶变换的公式,在第 1 节中有 具体的介绍。如果 x(n) = cos(nπ ) 是一个 N 为 12 的序列,利用 MATLAB 计算其 DFT 并画 6 出图形。 【例 10.4】在 MATLAB 中 x(n) = cos(nπ ) (N 为 12)DFT 的实现。 6 MATLAB 程序代码如下: N=12;%序列长度 n=0:N-1;%时域取样 xn=cos(pi*n/6);%产生序列 k=0:N-1;%频域取样 wn=exp(-j*2*pi/N); nk=n'*k; wnnk=wn.^nk; xk=xn*wnnk %计算 DFT figure(1)%画图 stem(n,xn) figure(2) stem(k,abs(xk)) 运行后得到的结果如下: xk = Columns 1 through 7 –378– 第 10 章 信号处理工具箱 -0.0000 6.0000 + 0.0000i -0.0000 - 0.0000i -0.0000 - 0.0000i -0.0000 - 0.0000i -0.0000 - 0.0000i -0.0000 - 0.0000i Columns 8 through 12 0.0000 - 0.0000i 0.0000 - 0.0000i 0.0000 - 0.0000i 0.0000 - 0.0000i 6.0000 + 0.0000i 图 10-11 有限序列和序列的 DFT 同样由 IDFT 公式也可以编写相应的程序: N=12;%序列长度 n=0:N-1; k=0:N-1; wn=exp(-j*2*pi/N); nk=n'*k; wnnk=wn.^(-nk); xn=(xk*wnnk)/N;%计算 IDFT figure(1)%画图 stem(k,abs(xk)) figure(2) stem(n,real(xn)) 在进行快速傅立叶变换 FFT 的操作时可以调用内部函数 fft,速度比较快。DFT 和 FFT 在信号处理中有着重要的应用,可以进行卷积运算,实现线性时不变系统等。 【例 10.5】设 x(n) 长度为 N1 , h(n) 长度为 N 2 ,计算两者的线性卷积,其步骤如下: • 将 x(n) 和 h(n) 通过增加零值延长到 N = N1 + N 2 −1; • 利用 FFT 计算 x(n) 和 h(n) 各自 N 点的 DFT; • 计算Y (k) = X (k)H (k) ; • 利用 IFFT 计算 Y (k) 便可得到卷积结果 y(n) 。 –379– MATLAB 7.0 从入门到精通 对于特定的序列,MATLAB 程序代码如下: x=[2,3,1,4,5]; h=[2,1,7,4,5,7,2,3]; lenx=length(x); lenh=length(h); N=lenx+lenh-1; xk=fft(x,N); hk=fft(h,N); yk=xk.*hk; y=real(ifft(yk)); plot(y); xlabel('n'); ylabel('y(n)'); title('x(n)*h(n)'); grid 运行结果如图 10-12 所示。 图 10-12 应用 FFT 计算线性卷积 10.3.3 Z 变换的 MATLAB 实现 有关 Z 变换的定义和性质在 10.1 节中已有介绍,对于 Z 变换为 X (z) 的序列,MATLAB 的表示是通过 X (z) 的系数实现的,Z 变换经常用到 deconv、residuez 和 freqz 等函数。 【例 10.6】计算 X (z) = (1 − 1 0.9z −1 )2 (1 + 0.9z −1 ) 其 MATLAB 程序代码如下: –380– z > 0.9 的逆 Z 变换。 b=1; a=poly([0.9 0.9 -0.9]); [R,P,C]=residuez(b,a) 执行完毕的结果代码为: R= 0.2500 0.5000 0.2500 P= 0.9000 0.9000 -0.9000 C= [] 因此得到下面的表达式: 第 10 章 信号处理工具箱 X (z) = 0.25 1 − 0.9z −1 + 0.5 (1 − 0.9z −1 )2 + 1 0.25 + 0.9z −1 z > 0.9 相应的逆 Z 变换为下面的表达式: x(n) = 0.25(0.9)n u(n) + 5 (n + 1)(0.9)n+1u(n +1) + 0.25(−0.9)n u(n) 9 = 0.75(0.9)n u(n) + 0.5n(0.9)n+1u(n) + 0.25(−0.9)n u(n) 【例 10.7】用 MATLAB 实现由系统的差分方程求出系统的冲激响应,画出其幅度相位响 应,并指出系统的零、极点,差分方程为: y(n) = y(n −1) − 0.6 y(n − 2) + x(n) − 2x(n −1) MATLAB 程序代码如下: %由系统的差分方程求出系统的冲激响应,画出其幅度、相位响应,并指出系统的零极点 A=[1,-1,0.6]; B=[1,-2]; %指出系统的零极点 [R,p,C]=residuez(B,A) %求系统的冲激响应 n=[-10:50]; x=[(n)==0]; h=filter(B,A,x); figure(1); plot(n,h); xlabel('n'); –381– MATLAB 7.0 从入门到精通 ylabel('h(n)'); title('系统的冲激响应'); %求其幅度相位响应 [H,W]=freqz(B,A,100); magH=abs(H); phaH=angle(H); figure(2); plot(W,magH); xlabel('频率'); ylabel('幅度'); title('幅度响应'); figure(3); plot(W,phaH); xlabel('频率'); ylabel('幅度'); title('相位响应'); 可得系统的零极点为: R= 0.5000 + 1.2677i 0.5000 - 1.2677i p= 0.5000 + 0.5916i 0.5000 - 0.5916i C= [] 系统的冲激响应、幅度响应和相位响应如图 10-13 所示。 –382– 第 10 章 信号处理工具箱 图 10-13 系统的冲激响应、幅度响应和相位响应 此外,Z 变换还可用于差分方程的求解。 10.3.4 FIR 滤波器的 MATLAB 实现 基于 MATLAB 的 FIR 滤波器的设计有多种方法,包括窗函数法(对应的 MATLAB 函数 有 fir1、fir2、kaiserord)、最优化设计法(对应的 MATLAB 函数有 firls、remez、remezord)、 最小二乘约束设计法(fircls、fircls1)、非线性相位滤波器设计法(cremez)和升余弦方法 (firrcos)本节将举例说明 fir1、fir2、kaiserord 和 firls、remez 如何用于滤波器的设计。 【例 10.8】设计一个阶数为 48,通带范围为 0.35≤ w ≤0.65 的带通 FIR 线性相位滤波器, 并分析它的频率特性。 MATLAB 程序代码如下: b=fir1(48,[0.35 0.65]); freqz(b); 得到的频率特性如图 10-14 所示。 图 10-14 利用 fir1 设计的带通滤波器的频率响应 –383– MATLAB 7.0 从入门到精通 【例 10.9】设计一个 60 阶的滤波器,要求设计的滤波器 0~π / 8 的幅度为 1,在π / 8 ~ 2π / 8 的幅度为 1/2,在 2π / 8 ~ 4π / 8 的幅度响应为 1/4,在 4π / 8 ~ 6π / 8 的幅度响应为 1/6,在 6π / 8 ~π 的响应为 1/8,并且画出理想滤波器和设计得到的滤波器的幅度频率响应 进行比较。 MATLAB 程序代码如下: f=[0 0.125 0.125 0.250 0.250 0.500 0.500 0.750 0.750 1.00]; m=[1 1 0.5 0.5 0.25 0.25 1/6 1/6 0.125 0.125]; b=fir2(60,f,m); [h,w]=freqz(b); plot(f,m,w/pi,abs(h)) grid on; title('设计滤波器和理想滤波器幅度频率特性比较'); xlabel('归一化频率(xfs)'); ylabel('幅度'); 得到的频率响应如图 10-15 所示。 图 10-15 利用 fir2 设计的任意频率响应的滤波器 【例 10.10】利用凯塞窗函数设计一个长度为奇数的带通滤波器(长度为奇数对应滤波器 是偶阶的,由此能满足带通滤波器的要求),通带范围是 1300Hz~2210Hz,阻带范围是 0Hz~ 1000Hz、2410Hz~4000Hz,阻带的波纹最大为 0.01,通带的波纹最大为 0.05,信号的采样频 率为 8000Hz。 MATLAB 程序代码如下: fsamp=8000; fcuts=[1000 1300 2210 2410]; mags=[0 1 0]; devs=[0.01 0.05 0.01]; [n,wn,beta,ftype]=kaiserord(fcuts,mags,devs,fsamp); –384– n=n+rem(n,2); hh=fir1(n,wn,ftype,kaiser(n+1,beta),'noscale'); [H,f]=freqz(hh,1,1024,fsamp); plot(f,abs(H)); grid on 第 10 章 信号处理工具箱 得到的滤波器的阶数为 90,滤波器的截止频率分别为 0.2875 和 0.5775,凯塞窗函数的 β 值为 3.3953。滤波器的频率响应如图 10-16 所示。 图 10-16 利用 kaiserord 设计的滤波器的频率响应 【例 10.11】分别用函数 firls 和 remez 设计一个 30 阶的 FIR 低通滤波器,通带边界频率 为 0.4 * π ,幅值为 3,阻带边界频率为 0.5 * π ,幅值为 1,并比较两函数设计的 FIR 滤波器。 注意:函数 firls 是 fir1 和 fir2 的扩展,其基本设计准则是利用最小二乘法使期望的频率响应和实 际的频率响应之间的整体误差最小。函数 remez 实现 Parks-Mcclellan 算法,它利用 Remez 交换法和 Chebyshev 近似理论来设计滤波器,使实际频率响应拟合频率响应达到最优。 MATLAB 程序代码如下: n=30; f=[0 0.4 0.5 1]; a=[1 1 0 0]; b=firls(n,f,a); [h,w]=freqz(b); bb=remez(n,f,a); [hh,w]=freqz(bb); axes('position',[0.2 0.2 0.5 0.5]); plot(w/pi,abs(h),'b-',w/pi,abs(hh),'r--'); xlabel('frequency(pi)'); –385– MATLAB 7.0 从入门到精通 ylabel('magnitude'); title('direct IIR design-yulewalk'); legend('firls','remez'); grid 程序运行结果如图 10-17 所示。 图 10-17 FIR 滤波器频率响应 可见用 firls 设计的滤波器在整个频率范围内均具有良好的频率响应,但理想频率响应和 实际频率响应的误差在带区内不均匀,且在边界频率处误差较大,而用函数 remez 设计的滤 波器在通带内有等波纹特性,在边界频率 0.4 * π 和 0.5 * π 处及过渡带内更接近于理想频响。 10.3.5 IIR 滤波器的 MATLAB 实现 IIR 滤波器的设计主要有模拟滤波器变换法(经典设计法)、直接设计法和最大平滑滤波 器设计法三种方法。本节将列举具有代表性的例程来说明三种方法的应用。 • 经典设计法是基于模拟滤波器的变换原理,首先根据滤波器的技术指标设计出相应的 模拟滤波器,然后再离散化为满足给定技术指标的数字滤波器。在 MATLAB 中对应 的工具函数有完全设计函数—butter、cheby1、cheby2、ellip、besself;阶数估计函 数—buttord、cheb1ord、cheb2ord、ellipord;低通模拟原型滤波器函数—buttap、 cheb1ap、cheb2ap、ellipap;频率转换函数—lp2lp、lp2bp、lp2hp、lp2bs;滤波器离 散化函数—bilinear、impinvar。 • 直接设计法是在离散域内用最小二乘法逼近给定的幅频特性,在 MATLAB 中对应的 工具函数是 yulewalk。 • 最大平滑滤波器设计法是设计一般化低通滤波器,其零点数多于极点,在 MATLAB 中对应的工具函数是 maxflat。 【例 10.12】经典法设计滤波器的设计有脉冲响应不变法和双线性变换法两种方式。 • 用椭圆滤波器原型设计一个低通滤波器,满足ω p = 0.2 * π 、Rp = 0.5dB 、ωs = 0.3* π 、 –386– As = 20dB ,采用脉冲响应不变法。 MATLAB 程序代码如下: wp=0.2*pi; ws=0.3*pi; rp=0.5; rs=20; [n,wn]=ellipord(wp,ws,rp,rs,'s') [z,p,k] = ellipap(n,rp,rs); w = logspace(-1,1,1000); h = freqs(k*poly(z),poly(p),w); semilogx(w,abs(h)); grid 运行结果如图 10-18 所示。 第 10 章 信号处理工具箱 图 10-18 椭圆低通滤波器的频响特性 • 采用双线性变换法设计带通 Chebyshev Ⅰ型数字滤波器,要求通带边界频率为 100~ 200Hz;通带纹波小于 3dB;阻带衰减大于 30dB;过渡带宽为 30Hz;采样频率为 1000Hz。 MATLAB 程序代码如下: fs=1000; wp=[100 200]*2/fs; ws=[30 300]*2/fs; rp=3; rs=30; Nn=128; [N,wn]=cheb1ord(wp,ws,rp,rs) [b,a]=cheby1(N,rp,wn) –387– MATLAB 7.0 从入门到精通 freqz(b,a,Nn,fs) 程序运行结果如图 10-19 所示。 图 10-19 Chebyshev Ⅰ型数字滤波器的频率特性图 【例 10.13】用直接法设计一个多频带数字滤波器,幅频响应值如下: f=[0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1]; m=[0 0 1 1 0 0 1 1 0 0 0]; 采用函数 yulewalk 来实现,具体操作步骤如下: • 计算与分子多项式相应的幅值平方响应的辅助分子式和分母式; • 由辅助分子式和分母式计算完全的频率响应; • 计算滤波器的脉冲响应; • 采用最小二乘法拟合脉冲响应,最终求得滤波器的分子多项式。 MATLAB 程序代码如下: oder=10; f=0:0.1:1; m=[0 0 1 1 0 0 1 1 1 0 0]; [b,a]=yulewalk(oder,f,m) [h,w]=freqz(b,a,128) axes('position',[0.2 0.2 0.4 0.4]); plot(f,m,'b-',w/pi,abs(h),'m--'); xlabel('frequency(pi)'); ylabel('magnitude'); title('direct IIR design-yulewalk'); legend('理想图形','实际图形',1); grid 程序运行结果如图 10-20 所示。 –388– 第 10 章 信号处理工具箱 图 10-20 直接设计的多频带滤波器 【例 10.14】用 maxflat 函数设计一个通用 Butter-Worth 低通滤波器,满足系统函数分子阶 数为 8 阶,系统函数分母阶数为 3 阶,截止频率为1* π 。 MATLAB 程序代码如下: nb=8; na=3; wn=0.6; [b,a]=maxflat(nb,na,wn,'plots') maxflat(nb,na,wn,'trace') 程序运行结果如下: b= 0.1650 a= 1.0000 Table: 0.5048 -0.1813 0.4100 -0.1134 -0.2329 -0.0244 0.2073 -0.3006 0.0202 -0.0043 0.0004 L M N wo_min/pi wo_max/pi 8.0000 7.0000 6.0000 5.0000 0 1.0000 2.0000 3.0000 3.0000 3.0000 3.0000 3.0000 0 0.2919 0.4021 0.5000 0.2919 0.4021 0.5000 0.5979 4.0000 4.0000 3.0000 0.5979 0.7081 3.0000 5.0000 3.0000 0.7081 1.0000 ans = 0.1650 0.5048 0.4100 -0.1134 -0.2329 由上述代码得到如图 10-21 所示的图形。 -0.0244 0.0202 -0.0043 0.0004 –389– MATLAB 7.0 从入门到精通 1 Frequency response Magnitude 0.5 Imaginary 0 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 Pole-zero plot 1 ω/π 4 Group delay 0.5 3 Samples 0 <- deg 4 2 -0.5 1 -1 0 -1 -0.5 0 0.5 1 0 0.5 1 Real ω/π 图 10-21 最大平滑 Butter-Worth 低通滤波器特性 –390– 第 11 章 图像处理工具箱 在上一章学习了 MATLAB 信号处理方面的应用之后,本章将在对各种图像处理方法的 理论作系统讲解的基础上,详细介绍了 MATLAB 图像处理工具箱函数的使用方法,并给出 了大量的应用实例,使读者能够更好地掌握和使用 MATLAB 图像处理工具箱函数进行图像 处理。 11.1 图像处理工具箱介绍 11.1.1 常用图像格式 图像格式指的是存储图像采用的格式,不同的操作系统、不同的图像处理软件,所支持 的图像格式都有可能不同。在实际应用中经常会遇到的图像格式有 BMP、GIF、TIFF、PCX、 JPEG、PSD、PCD 和 WMF 等。由于篇幅的限制,本节仅介绍其中的一部分。 1.BMP 文件 BMP 文件是 Microsoft Windows 所定义的图像文件格式,最早应用在微软公司的 Microsoft Windows 窗口系统中。BMP 图像文件具有以下特点: • 只存放一幅图像; • 只能存储单色、16 色、256 色和真彩色 4 种图像数据; • 图像数据有压缩和非压缩两种处理方式; • 调色板的数据存储关系较为特殊,存储格式不是固定的,而是与文件头的某些具体参 数(如像素位 bbp、压缩算法等)密切相关的。其中,Windows 设有 RLE4 和 RLE8 两种压缩方式,RLE4 只能处理 16 色图像数据,RLE8 则只能压缩 256 色图像数据。 BMP 图像文件的文件结构可分为表头、调色板和图像数据三部分。其中表头长度固定为 54 个字节,只有真彩色 BMP 图像文件内没有调色板数据,其余不超过 256 种颜色的图像文 件都必须有调色板信息。 BMP 文件头数据结构含有 BMP 文件的类型、大小和打印格式等信息。在 Windows 种对 其进行了定义,其定义如下: Typedef struct tag BITMAPFILEHEADER{ WORD bftype; /*位图文件的类型,必须为 BMP DWORD bfSize; /*位图文件的大小,以字节为单位 WORD bfReserved1; /*位图文件保留字,必须为 0 WORD bfReserved2; /*位图文件保留字,必须为 0 MATLAB 7.0 DWORD bfoffBits; /*位图阵列的起始位置,以相对于位图文件头的偏移量表示 }BITMAPFILEHEADER; 可见,位图信息数据结构含有位图文件的尺寸和颜色等信息。位图阵列记录了位图的每 一个像素值。在生成位图文件时,Windows 从位图的左下脚开始逐行扫描位图,将位图的像 素值一一记录下来,组成了位图阵列。 2.GIF 文件 GIF(Graphics Interchange Format)图像文件格式是 CompuServe 公司最先在网络中用于 在线传输图像数据。GIF 图像文件经常用于网页的动画、透明等特技制作。该文件具有以下 特点: • 文件具有多元化结构,能够存储多张图像,并可以进行多图像的定序或覆盖,交错屏 幕绘图以及文本覆盖等功能。 • 调色板数据有通用调色板和局部调色板之分。 • 采用了 LZW 压缩法。 • 图像数据一个字节存储一个像素点。 • 文件内的各种图像数据区和补充区多数没有固定的数据长度和存储位置,为了方便程 序寻找数据区,就以数据区的第一个字节作为标识符,以使程序能够判断读到哪种数 据区。 • 图像数据有顺序排列和交叉排列两种方式。 • 图像最多只能存储 256 色图像。 GIF 图像文件结构一般由表头、通用调色板、图像数据区以及 4 个补充区这 7 个数据单 元组成。其中,表头和图像数据区是不可缺少的单元,通用调色板和其余的 4 个补充区是可 选择的内容。 3.TIF 文件 TIF(Tag Image File Format)图像文件格式是现有图像文件格式中最复杂的一种,它是 由 Aldus 公司与微软公司开发设计的图像文件格式,提供了各种信息存储的完备手段。其主 要特点如下: • 应用指针功能,实现多幅图像存储; • 文件内数据没有固定的排列顺序,但规定表头必须在文件前端,标识信息区和图像数 据区在文件中可以任意存放; • 可定制私人用的标识信息; • 能够接受除了一般图像处理 RGB 模式之外的 CMYK、YcbCr 等多种不同的图像模式; • 可存储多份调色板数据,其调色板的数据类型和排列顺序较为特殊; • 能够提供多种不同的压缩数据的方法; • 图像数据可分割成几个部分进行分别存档。 TIF 图像文件主要由表头、标识信息区和图像数据区 3 个部分组成。其中,文件内固定 只有一个位于文件前端表头,表头由一个标志参数指出标识信息区在文件中的存储地址,标 识信息区有多组用于存储图像数据区的地址。每组标识信息长度固定为 12 个字节,前 8 个字 节分别代表信息的代号(2 个字节)、数据类型(2 个字节)、数据量(4 个字节),最后 4 个 –392– 字节用于存储数据值或标识参数。 第 11 章 图像处理工具箱 4.JPEG 格式 JPEG(Joint Photographic Experts Group)是对精致灰度或彩色图像的一种国际压缩标准, 其全称为“连续色调静态图像的数字压缩和编码”,已在数字照相机上得到了广泛应用,当选 用有损压缩方式时可以节省相当大的空间。 JPEG 标准只是定义了一个规范的编码数据流,并没有规定图像数据文件的格式。Cube Microsystems 公司定义了一种 JPEG 文件交换格式(JFIF-File Interchange Format)。JFIF 图像 是一种使用灰度标识,或者使用 Y,Cb,Cr 分量彩色表示的 JPEG 图像。它包含一个与 JPEG 兼容的头。一个 JFIF 文件通常包含单个图像,图像可以是灰度的(其中的数据为单个分量), 也可以是彩色的。 11.1.2 MATLAB 7.0 图像类型 图像类型是指数组数值与像素颜色之间定义的关系,它与图像格式概念有所不同,在 MATLAB 7.0 图像处理工具箱中,有 5 种类型的图像,下面分别介绍其基本情况。 1.二进制图像 在一幅二进制图像中,每一个像素将取两个离散数值(0 或 1)中的一个,从本质上说, 这两个数值分别代表状态“开”或“关”。 二进制图像使用 unit8 或双精度类型的数组类存储。由于 unit8 数组使用的内存较小,故 unit8 类型的数组通常比双精度类型的数组性能要好。在图像处理工具箱中,任何一个返回一 幅二进制图像的函数均使用 unit8 逻辑数组存储该图像,并且使用一个逻辑标志来标识 unit8 逻辑数组的数据范围。若逻辑状态为“开”,则数组范围为[0,1];若为“关”,则数组范围为 [0,255]。图 11-1 是一幅典型的二进制图像实例。 图 11-1 典型的二进制图像实例 –393– MATLAB 7.0 2.索引图像 索引图像是一种把像素值直接作为 RGB 调色板下标的图像。在 MATLAB 7.0 中,索引 图像包含一个数据矩阵 X 和一个颜色映射矩阵 map。其中,数据矩阵可以是 unit8、unit16 或 双精度类型的,颜色映射矩阵 map 是一个 m×3 的数据帧里,其中每个元素的值均为[0,1]之 间的双精度浮点类型数据,map 矩阵的每一行分别标识红色、绿色和蓝色的颜色值。索引图 像可把像素值直接映射为调色板数据,每一个像素的颜色通过使用 X 的数值作为 map 的下标 来获得,如值 1 指向 map 中的第一行,值 2 指向第二行,依次类推。 颜色映射通常与索引图像存储在一起,当装载图像时,MATLAB 7.0 自动将颜色映射表 与图像同时装载。图 11-2 显示了索引图像的结构。该图像中的像素用整数类型标识,这个整 数将作为存储在颜色映射表中的颜色数据的指针。 图 11-2 索引图像实例 图像矩阵与颜色映射表之间的关系依赖于图像数据矩阵的类型。如果图像数据矩阵是双 精度类型,则数据 1 指向 map 矩阵中的第一行,数据值 2 指向 map 中的第二行,依次类推; 如果图像矩阵是 unit8 或 unit16 类型时,将产生一个偏移量,即数据 0 标识矩阵 map 中的第 一行,数据 1 将指向 map 中的第二行,依次类推。在如图 11-2 所示的图像中,图像矩阵用 的是双精度类型,无偏移量,数值 5 指向颜色映射表中的第五行。 3.灰度图像 灰度图像通常由一个 unit8、unit16 或双精度类型的数组来描述,其实质是一个数据矩阵 I,该矩阵中的数据均代表了一定范围内的灰度级,每一个元素与图像的一个像素点相对应, 通常 0 代表黑色,1、255 或 65635(针对不同存储类型)代表白色。大多数情况下,灰度图 像很少和颜色映射表一起保存,但是在显示灰度图像时,MATLAB 7.0 仍然在后台使用预定 义的默认灰度颜色映射表。图 11-3 为一个典型的双精度灰度图像。 –394– 第 11 章 图像处理工具箱 图 11-3 典型的双精度灰度图像 4.多帧图像 多帧图像是一种包含多幅图像或帧的图像文件,又称为多页图像或图像序列,它主要用 于需要对时间或场景集合进行操作的场合,例如磁谐振图像切片或电影帧等。在 MATLAB 7.0 中,它是一个四维数组,其中第四维用来指定帧的序号。 在 MATLAB 7.0 图像处理工具箱中提供了同一个数组中存储多幅图像的支持,每一幅单 独的图像成为一帧。如果一个数组中包含多帧,那么这些图像在四维中是相联系的。在一个 多帧图像数组中,每一幅图像必须有相同的大小和颜色分量。在多帧图像中,每一幅图像还 要使用相同的调色板。另外,图像处理工具箱中的许多函数(如 imshow)只能对多帧图像矩 阵的前两维或三维进行操作,也可以对四维数组使用这些函数,但是必须单独处理每一帧。 如果将一个数组传递给一个函数,并且数组的维数超过该函数设计的操作位数,那么得到的 结果是不可预知的。 5.RGB 图像 RGB 图像又称为真彩图像,它是利用 R、G、B 三个分量标识一个像素的颜色,R、G、 B 分别代表红、绿、蓝 3 种不同的颜色,通过三基色可以合成出任意颜色。所以对一个尺寸 维 n×m 的彩色图像来说,在 MATLAB 7.0 中则存储一个 n×m×3 的多维数据数组,其中数 组中的元素定义了图像中每一个像素的红、绿。蓝颜色值。值得注意的是 RGB 图像不使用 调色板,每一个像素的颜色由存储在相应位置的红、绿、蓝颜色分量的组合来确定,图形文 件格式把 RGB 图像存储维 24 位的图像,红、绿、蓝分量分别占用 8 位,因而图像理论上可 以有 224=16 777 216 种颜色,由于这种颜色精度能够再现图像有真实色彩,故称 RGB 图像 为真彩图像。 图 11-4 为一幅典型的双精度 RGB 图像,在此图种,为了确定像素(2,3)的颜色,需 要查看一组数据 RGB(2,3,1:3)。假设(2,3,1)数据为 0.5176,(2,3,2)数值为 0.1608,(2,3,3)数值为 0.0627,则像素(2,3)的 RGB 颜色为(0.5176 红色,0.1608 绿 色,0.0627 蓝色)。 –395– MATLAB 7.0 图 11-4 典型的双精度 RGB 图像实例 11.1.3 MATLAB 7.0 图像类型转换 在有些图像操作中,需要对图像的类型进行转换。比如要对一副索引图像的彩色图像进 行滤波,首先应该将其转换成 RGB 图像,此时在对 RGB 图像使用滤波器时,MATLAB 7.0 将恰当地滤掉图像中的部分灰度值。如果不将索引图像进行转换,MATLAB 7.0 则对图像调 色板的序号进行滤波,这样得到的结果将没有任何意义。下面对一些 MATLAB 7.0 图像处理 工具箱中常用的类型转换进行介绍。 1.图像颜色浓淡处理(图像抖动) dither 函数通过抖动算法转换图像类型,其语法格式为: • X = dither(RGB,map):通过抖动算法将真彩色图像 RGB 按指定的颜色图(调色板) map 转换成索引色图像 X。 • X = dither(RGB,map,Qm,Qe):利用给定的参数 Qm,Qe 从真彩色图像 RGB 种产生索 引色图像 X。Qm 表示沿每个颜色轴反转颜色图的量化(即对于补色各颜色轴)的位 数,Qe 表示颜色空间计算误差的量化位数。如果 Qe thresh)% 程序运行结果如图 11-13 所示。 –411– MATLAB 7.0 图 11-13 图像特征识别示例 11.4.2 数字图像的离散余弦变换 离散余弦变换的变换核为余弦函数,计算速度比较快,有利于图像雅座和其他处理。在 大多数情况下,离散余弦变换(DCT)用于图像的压缩操作中。JPEG 图像格式的压缩算法 采用的是 DCT。 1.离散余弦变换的定义 一维离散余弦正反变换公式如下: ∑ F (k) = N −1 2 f (n) cos π (2n + 1)k n=0 2N n, k = 0,1,Λ , N −1 ∑ f (n) = 1 N−1 F (k) cos π (2n + 1)k N k=0 2N n, k = 0,1,Λ , N −1 类似于一位离散余弦变换,二维离散余弦正变换公式为: ∑ ∑ F (u, v) = M c(u)c(v) −1 N −1 f (x, y) cos π (2x + 1)u cos π (2 y + 1)v x=0 y=0 2M 2N u = 0,1,Λ , M −1;v = 0,1,Λ , N −1 –412– 第 11 章 图像处理工具箱  c(u) =  1/ M 其中:  2/M  c(v) =  1/ N  2/N u=0 u = 1,2Λ , M −1 u=0 u = 1,2Λ , N −1 二维离散余弦反变换公式为: ∑ ∑ f (x, y) = M −1 N −1 c(u)c(v)F (u, v) cos π (2x + 1)u cos π (2 y + 1)v x=0 y=0 2M 2N x = 0,1,Λ , M −1; y = 0,1,Λ , N −1 其中 x,y 为空间域采样值,u,v 为频域采样值。通常数字图像用像素方阵标识,即 M = N。在这种情况下,二维离散余弦正反变换可以简化为: ∑ ∑ F (u, v) = N −1 c(u)c(v) N −1 f (x, y) cos π (2x + 1)u cos π (2 y + 1)v x=0 y=0 2N 2N u = 0,1,Λ , N −1;v = 0,1,Λ , N −1 ∑ ∑ f (x, y) = N −1 N −1 c(u)c(v)F (u, v) cos π (2x + 1)u cos π (2 y + 1)v x=0 y=0 2N 2N x = 0,1,Λ , N −1; y = 0,1,Λ , N −1  其中, c(u) =  1/ 2 1 u = 0或v = 0 u, v = 1,2Λ , M −1 在 MATLAB 7.0 中, c(u)c(v)F (u, v) cos π (2x +1)u cos π (2 y +1)v 称为离散余弦变换 2N 2N 的基础函数。这样 DCT 系数 F (u, v) 可以看成是每一个基础函数的加权。例如,对应一个 8×8 矩阵,它的 64 个基础函数如图 11-14 所示。 –413– MATLAB 7.0 图 11-14 8×8 矩阵的 64 个基础函数 在图 11-14 中,这些基础函数的水平频率从左到右增长,垂直频率从上到下增长。位于 图像左上方的定值基础函数通常被成为 DC 基础函数,相应的 DCT 系数称为 DC 系数。 2.MATLAB 7.0 提供的 DCT 变换函数 (1)dct2 函数 该函数用于实现图像的二维离散余弦变换,其语法格式为: • B = dct2(A):返回图像 A 的二维离散余弦变换值,它的大小与 A 相同,且各元素为离 散余弦变换的系数 F(k1,k2)。 • B = dct2(A,m,n) • B = dct2(A,[m,n]):在对图像 A 进行二维离散余弦变换之前,先将图像 A 补零至 m×n。 如果 m 和 n 比图像 A 小,则进行变换之前,将图像 A 剪切。 (2)idec2 函数 该函数可以实现图像的二维离散余弦反变换,其语法格式为: • B = idct2(A):返回图像 A 的二维离散余弦反变换值,它的大小与 A 相同。 • B = idct2(A,m,n) • B = idct2(A,[m,n]):在对图像 A 进行二维离散余弦反变换之前,先将图像 A 补零至 m×n。如果 n 和 n 比图像 A 小,则进行变换之前,将图像 A 剪切。 (3)dctmtx 函数 该函数用于计算二维离散余弦变换矩阵,其语法格式为: • D = dctmtx(n):返回 n×n 的 DCT 变换矩阵,如果矩阵 A 的大小为 n×n,D×A 是 A 矩阵每一列的 DCT 变换值,D’×A 是 A 每一列的反变换值。如果矩阵 A 为 n×n 的方 阵,则 A 的 DCT 变换可以用 D×A×D’来计算。特别是对于 A 很大的情况,比利用 dct2 计算二维离散 DCT 变换要快。 –414– 3.离散余弦变换的 MATLAB 7.0 实现 第 11 章 图像处理工具箱 在 MATLAB 7.0 中,函数 dct2 核函数 idct2 分别用于进行二维 DCT 变换和二维 DCT 反 变换。下面举例来说明二维余弦正反变换在 MATLAB 7.0 中的实现,程序代码如下: %载入图像 RGB = imread(’autumn.tif’); figure(1); imshow(RGB); %将真彩图转换为灰度图 I = rgb2gray(RGB); figure(2); imshow(I); %进行余弦变换 J = dct2(I); figure(3); imshow(log(abs(J)),[]); colormap(jet(64)); colorbar; %将 DCT 变换值小于 11 的元素设为 0 J(abs(J)<11) = 0; %进行余弦反变换 K = idct2(J)/255; figure(4); imshow(K) 程序运行输入结果如图 11-15 所示,其中左上为原始图像,右上为对应的灰度图像,左 下为余弦变换系数,右下为余弦反变换恢复图像。 10 5 0 -5 图 11-15 余弦变换与反变换图示 –415– MATLAB 7.0 4.离散余弦变换的应用 离散余弦变换在图像压缩中具有广泛的应用,下面仅介绍一个用 DCT 压缩的例子。 在 JPEG 图像压缩算法中,首先将输入图像分解为 8×8 或 16×16 的图像块,然后对每 个图像块进行二维 DCT 变换,最后将变换得到的量化的 DCT 系数进行变化或传送,形成压 缩后的图像格式。在接收端,将量化的 DCT 系数进行解码,并对每一个 8×8 块或 16×16 块进行二维 DCT 反变换,最后将操作完成后的块组成一个单个的图像。至此,完成图像的压 缩和解压缩过程。对于一幅典型的图像而言,大多数的 DCT 系数的值非常接近于 0,如果舍 弃这些接近于 0 的 DCT 系数值,在重构图像时并不会因此而带来画面质量的显著下降。故利 用 DCT 进行图像压缩可以节约大量的存储空间。 下面是一个展示了如何把如图 11-16 左边所示的输入图像划分成 8×8 的图像块,计算它 们的 DCT 系数,并且只保留 64 个 DCT 系数中的 11 个,然后对每个图像块利用这 11 个系数 进行 DCT 反变换来重构图像的程序: I = imread(’cameraman.tif’); I = im2double(I); %产生二维 DCT 变换矩阵 T = dctmtx(8); %计算二维 DCT B = blkproc(I,[8 8],’P1*x*P2’,T,T’); %二值掩模,用来压缩 DCT 系数 mask = [1 1 1 1 0 0 0 0 11100000 11000000 10000000 00000000 00000000 00000000 0 0 0 0 0 0 0 0]; %只保留 DCT 变换的 11 个系数 B2 = blkproc(B,[8 8],’P1.*x’,mask); %DCT 反变换,用来重构图像 I2 = blkproc(B2,[8 8],’P1*x*P2’,T’,T); subplot(1,2,1) imshow(I), subplot(1,2,2) imshow(I2) 运行结果如图 11-16 所示。 –416– 第 11 章 图像处理工具箱 图 11-16 离散余弦变换在图像压缩应用示例 11.4.3 其他变换技术 除了上面介绍的傅立叶变换和离散余弦变换外,在图像处理中还有几种使用比较广泛的 变换技术,它们是离散沃尔什变换、离散哈达玛变换以及 Randon 变换。这里只简略的介绍 每种变换的基本思想,详细的内容以及使用方法请有兴趣的读者参看书后的附录以及 MATLAB 7.0 自带的帮助文档。 1.离散沃尔什变换 傅立叶变换和离散余弦变换都是由正弦或余弦三角函数为基本的正交函数基,在快速算 法中要用到复数乘法、三角函数乘法,占用时间仍然较多。在某些领域,需要有更为有效和 便利的变换方法。沃尔什(Walsh)变换就是其中一种。它包括只有+1 和-1 两个数值所构成 的完备正交基。由于沃尔什函数基就是二值正交基,与数字逻辑的两个状态相对应,因而更 加适用于计算机处理。另外,与傅立叶变换相比,沃尔什变换减少了存储空间和提高了运算 速度,这一点对图像处理来说是至关重要的。特别是在大量数据需要进行实时处理时,沃尔 什变换更加显示出其优越性。 2.离散哈达玛变换 哈达玛(Hadamard)变换本质上是一种特殊排序的沃尔什变换,哈达玛变换矩阵也是一 个方阵,只包括+1 和-1 两个矩阵元素,各行和各列之间彼此是正交的,即任意二行相乘或二 列相乘后的各数之和必须为零。哈达玛变换核矩阵与沃尔什变换不同指出仅仅是行的次序不 同。哈达玛变换的最大优点在于它的变换核矩阵具有简单的递推关系,即高阶矩阵可以用两 个低阶矩阵求得。这个特点使人们更愿意采用哈达玛变换,不少文献中常采用沃尔什—哈达 玛变换这一术语。 3.Radon 变换 Radon 变换是计算图像在某一角度射线方向上投影的变换方法。我们知道,二维函数 f(x,y) 的投影是其在确定方向上的线积分。例如,f(x,y)在垂直方向上的二维线积分就是 f(x,y)在 x –417– MATLAB 7.0 轴上的投影;f(x,y)在水平上的二维线积分就是 f(x,y)在 y 轴上的投影。图 11-17 说明了一个简 单二维函数的水平和垂直投影。 图 11-17 二维函数的水平投影核垂直投影示意图 另外,可以沿任意角度θ对函数进行投影,即任意角度函数 f(x,y)的 Radon 变换可以表 达为: ∞ ∫ Rθ (x’) = f (x’ cosθ − y ’ sinθ , x’ sinθ + y ’ cosθ )dy ’ −∞ 其中 x’   y ’   =  cosθ − sinθ sinθ  x cosθ  y ,函数 f(x,y)的 Radon 变换几何示意图如图 11-18 所示。 图 11-18 函数 f(x,y)的 Radon 变换几何示意图 11.5 图像分析 MATLAB 7.0 的图像处理工具箱支持多种标准的图像处理操作,以方便用户对图像进行 分析和调整。这些图像处理操作主要包括: • 获取像素值及其统计数据; –418– • 分析图像,抽取其主要结构信息; • 调整图像,突出其某些特征或抑制噪声。 第 11 章 图像处理工具箱 11.5.1 像素值及其统计 MATLAB 7.0 的图像处理工具提供多个函数以返回与构成图像的数据值相关的信息,这 些函数能够以多种形式返回图像数据的信息,主要包括: • 选定像素的数据值(pixval 函数和 impixel 函数); • 沿图像中某个路径的数据值(improfile 函数); • 图像数据的轮廓图(imcontour 函数); • 图像数据的柱状图(imhist 函数); • 图像数据的摘要统计值(mean2 函数、std2 函数和 corr2 函数); • 图像区域的特征度量(imfeature 函数)。 1.像素选择 图像处理工具箱中包含两个函数可以返回用户指定的图像像素的颜色数据值。 (1)pixval 函数 当光标在图像上移动时,该函数以交互的方式显示像素的数据值。另外,该函数还可以 显示两个像素之间的 Euclidean 距离。 (2)impixel 函数 impixel 函数可以返回选中像素或像素集的数据值。用户可以直接将像素坐标作为该函数 的输入参数,或用鼠标选中像素。 例如,在下面的例子中,首先调用 impixel 函数,然后在显示的 canoe.tif 图像中用鼠标 选中 3 个点,代码如下: imshow canoe.tif vals = impixel 上面的代码运行后,得到如图 11-19 所示的运行界面。选中 3 个点后,按回车键,则命 令行中得到的结果如下: vals = 0.1922 0.1922 0.1608 0.5176 0 0 0.4824 0.3882 0.2902 值得注意的是在所得的结果中,对应于第 2 个像素(该像素位于小船上)的值为纯红色, 其绿色和蓝色成分均为 0;另外,对应索引图像,pixval 函数和 impixel 函数都将其显示为存 储在颜色映像中的 RGB 值而不是索引值。 –419– MATLAB 7.0 canoe.tif 图 11-19 impixel 函数的运行界面 2.强度描述图 在 MATLAB 7.0 图像处理工具箱中提供了 improfile 函数用于沿着图像中一条直线段路径 或直线路径计算并绘制其强度(灰度)值。例如下面的代码: I = fitsread(’solarspectra.fts’); imshow(I,[]); improfile 运行后,得到如图 11-20 所示的运行界面。确定直线段或直线路径后,按回车键,则得 到如图 11-21 所示的轨迹强度(灰度)图。 图 11-20 improfile 函数的运行界面(灰度图) 图 11-21 improfile 函数的运行结果 注意:图 11-21 中的峰值对应于图 11-20 中的黑色或白色。 下面的示例演示 improfile 函数如何处理 RGB 图像。具体的代码如下: imshow peppers.png improfile 运行后,得到如图 11-22 所示的运行界面。确定直线段或直线路径后,按回车键,则得 –420– 到如图 11-23 所示的轨迹强度图。 peppers.png 第 11 章 图像处理工具箱 图 11-22 improfile 函数的运行界面(RGB) 图 11-23 improfile 函数的运行结果(RGB) 由图 11-23 可以看出,improfile 函数绘制的强度图是将红色、绿色和蓝色分离开了,各 自表达为独立的线条图形。 3.图像轮廓图 在灰度图的轮廓图显示时,可以利用 MATLAB 7.0 图像处理工具箱中的 imcontour 函数。 该函数类似于 contour 函数,与 contour 函数相比,其功能更全。它能够自动设置坐标轴对象, 从而使得其方向和纵横比能够与所显示的图形相匹配。 例如下面的代码: I = imread(’rice.png’); subplot(1,2,1) imshow(I) subplot(1,2,2) imcontour(I,3) 运行后,分别在图形窗口中显示原始图像 rice.png 及其轮廓图,如图 11-24 所示。 50 100 150 200 250 50 100 150 200 250 图 11-24 图像轮廓图 –421– MATLAB 7.0 4.图像柱状图 图像柱状图可以用来显示索引图像或灰度图像中的灰度分布。可以利用 MATLAB 7.0 图 像处理工具箱中的 imhist 函数创建柱状图。下面以前面介绍的大米灰度图为例来创建该图的 柱状图。其代码设置如下: I = imread(’rice.png’); subplot(1,2,1) imshow(I) subplot(1,2,2) imhist(I,64) 代码运行后的结果如图 11-25 所示。由此可见,柱状图的峰值出现在 100 附近,这是因 为大米堆的背景色为深灰色所致。 4000 3500 3000 2500 2000 1500 1000 500 0 0 100 200 图 11-25 大米灰度图的柱状图 11.5.2 图像分析 在 MATLAB 7.0 中的图像分析技术可以提取图像的结构信息。例如,可以利用图像处理 工具箱中提供的 edge 函数来探测边界。这里所谓的边界,其实就是图像中包含的对象所对应 的位置。下面介绍几种常见图像分析函数。 1.灰度图像的边缘:edge 函数 该函数的语法如下: • BW = edge(I,method):返回与 I 大小一样的二进制图像 BW,其中元素 1 为发现 I 中的 边缘。Method 为下列字符串之一。 ’sobel’ 缺省值,用导数的 sobel 近似值检测边缘,那些梯度最大点返回边缘; ’prewitt’ 用导数的 prewitt 近似值检测边缘,在那些梯度最大点返回边缘; ’roberts’ 用导数的 roberts 近似值检测边缘,在那些梯度最大点返回边缘; –422– 第 11 章 图像处理工具箱 ’log’ 使用高斯滤波器的拉普拉斯运算对 I 进行滤波,通过寻找 0 相交检测边缘; ’zerocross’ 使用指定的滤波器对 I 滤波器后,寻找 0 相交检测边缘。 • BW = edge(I,method,thresh):用 thresh 指定灵敏度阈值,所有不强于 thresh 的边缘都被 忽略; • BW = edge(I,method,thresh,direction):对于’sobel’和’prewitt’方法指定方向。该函数中的 direction 为字符串,即’horizontal’标识水平方向,’vertical’表示垂直方向,’both’两个方 向(缺省值); • BW = edge(I,’log’,thresh,sigma):用 sigma 指定标准差。 2.行四叉树分解:qtdecomp 函数 将一块图像分成 4 块等大小的方块,然后判断每块是否满足同性质的标准,如果满足, 则不再分解,否则,再进行分成 4 块,并对每块应用测试标准,分解过程重复迭代下去,直 到满足标准。结果可能包含不同大小的块,改函数的语法结构如下: • S = qtdecomp(I):对灰度图像 I 进行四叉树分解,返回四叉树结构的稀疏矩阵 S。 • S = qtdecomp(I,threshold):如果块中元素最大值减去元素最小值大于 threshold,则分 解块。Threshold 为 0~1 之间的值。 • S = qtdecomp(I,threhold,mindim):如果块小于 mindim 就不再进行分解,无论其符合阈 值条件与否。 • S = qtdecomp(I,FUN):使用函数 FUN 确定是否分解块。 3.获取四叉树分解块值:qtdgetblk 函数 该函数的语法结构如下: • [VALS,R,C] = qtdgetblk(I,S,dim):VALS 中对应 dim×dim 块的取值取代 I 的四叉树分解 中的每个 dim×dim 块。S 为 qtdecomp 函数返回的稀疏矩阵,包含四叉树结构;VALS 是 dim×dim×k 数组,k 是四叉树分解的 dim×dim 块的数量。如果没有指定大小的块, 则返回一个空矩阵。R 和 C 为包含块左上角行列坐标的向量。 • [VALS,IDX] = qtdgetblk(I,S,dim):返回块左上角直线索引的向量 IDX。 4.设置四叉树分解块值:qtsetblk 函数 • J = qtsetblk(I,S,dim,VALS):用 VALS 中对应 dim×dim 块的值取代 I 的四叉树分解中的 每个 dim×dim 块。S 为 qtdecomp 函数返回的稀疏矩阵,包括四叉树结构;VALS 是 dim×dim×k 数组,k 是四叉树分解的 dim×dim 块数量。 5.实例 (1)图像分析中的灰度边缘检测实例 源代码设置如下: %调入与显示 RGB 图像 RGB = imread(’peppers.png’); isrgb(RGB); –423– MATLAB 7.0 figure(1); imshow(RGB); %RGB 图转换为灰度图像 I = rgb2gray(RGB); figure(2); imshow(I); colorbar(’horiz’); isgray(I); %边缘检测 ED = edge(I,’sobel’,0.08); figure(3) imshow(ED); 50 100 150 200 250 图 11-26 灰度边缘检测实例 运行结构如图 11-26 所示,图(a)为原始图像,图(b)为对应的灰度图,图(c)为检 测到的灰度边缘。 (2)Sobel 边界探测器和 Canny 边界探测器再图像分析中的应用实例 源代码设置如下: %读入原始图像 I = imread(’coins.png’); imshow(I) –424– 第 11 章 图像处理工具箱 %sobel 边界探测器 BW1 = edge(I,’sobel’); %canny 边界探测器 BW2 = edge(I,’canny’); figure imshow(BW1) figure, imshow(BW2) 运行结果如图 11-27 所示,图(a)为原始图像,图(b)为采用 sobel 探测器获得的结果, 图(c)为采用 canny 边界探测器获得的结果。 图 11-27 边界探测器在图像分析中的应用示例 11.5.3 图像调整 MATLAB 7.0 中的图像调整技术用于图像的改善。此处的改善有两个方面的含义,即客 观方面,例如提高图像的信噪比;主观方面,例如通过修正图像的颜色和强度(灰度)使其 某些特征更容易辨识。 1.灰度调整 在 MATLAB 7.0 中,相关的函数为 imadjust,该函数可用于调整灰度值或颜色图,下面 –425– MATLAB 7.0 介绍其用法。 • J = imadjust(I,[low high],[bottom top],gamma) 将灰度图像 I 转换为图像 J,使值从 low~high,与从 bottom~top 相匹配。值大于 high 或小于 low 的被剪去,即小于 low 的值与 bottom 相匹配,大于 high 的值与 top 相匹配。使 用该函数时可将[low high]或[bottom top]指定为空矩阵[],此时缺省值为[0 1]。gamma 用来 指定描述 I 和 J 值关系曲线的形状;gamma<1,越亮输出值越强;gamma>1,越亮输出值 越弱;缺省值 gamma=1,表示线性变换。gamma 大于 1、等于 1 和小于 1 的映射方式如图 11-28 所示。 • newmap = imadjust(map,[low high],[bottom top],gamma) 对索引图像的颜色图进行变换。如果[low high]和[bottom top]均为 2×3 矩阵,则 gamma 为 1×3 向量,imadjust 函数分别调整红、绿、蓝成分,调整后的颜色图 newmap 大小与原来 的 map 一样。 图 11-28 不同 gamma 对应的转移函数曲线 • RGB = imadjust(RGB1,...) 对于 RGB 图像 RGB1 的每个图像块进行调整。与调整颜色图一样,通过指定[low high] 和[bottom top]均为 2×3 矩阵,gamma 为 1×3 向量,对每个图像块可以使用不同的参数值。 如果 top | < | pentagram | hexagram | {none} ] 如果希望得到所有可设置的属性,或者希望得到能够接收字符串属性的所有可能取值, 使用对象句柄作为 set 函数的惟一参数即可实现: set(object_handle) 例如,对于一个曲面对象调用以上语句,MATLAB 7.0 将返回如下信息: CData CDataScaling: [ {on} | off] EdgeColor: [ none | {flat} | interp ] ColorSpec. EraseMode: [ {normal} | background | xor | none ] FaceColor: [ none | {flat} | interp | texturemap ] ColorSpec. LineStyle: [ {-} | -- | : | -. | none ] Visible: [ {on} | off ] set 函数的输出是一个结构体数组。例如下面的示例代码: a = set(gca); 上述语句中的返回值 a 是一个结构体,其域名是对象的属性名,域值是属性的可能取值。 如果需要获得坐标轴网线格式的可能取值,可以使用以下的语句: a.GridLineStyle MATLAB 7.0 将输出的代码如下: ans = '-' '--' ':' '-.' 'none' 注意:虽然属性名与大小写无关,但结构体域名与大小写有关,如果输入: a.GridLineStyle MATLAB 7.0 将产生一个错误信息。 12.2.3 属性值查询 使用 get 函数来查询一个或多个对象属性的当前取值。例如,使用以下语句检查当前坐 标轴 PlotBoxAspectRatio 属性的取值: get(gca,'PlotBoxAspectRatio') 执行结果代码如下: –442– 第 12 章 高级图形设计 ans = 111 虽然 MATLAB 7.0 能够列举所有属性的取值,但对于包含数据的属性,例如 Current 和 ColorOrder,MATLAB 7.0 仅列举该属性值的维数。例如下面的示例代码: AmbientLightColor = [1 1 1] Box = off CameraPosition = [0.5 0.5 2.23205] CameraPositionMode = auto CameraTarget = [0.5 0.5 0.5] CameraTargetMode = auto CameraUpVector = [0 1 0] CameraUpVectorMode = auto CameraViewAngle = [32.2042] CameraViewAngleMode = auto CLim: [0 1] CLimMode: auto Color: [0 0 0] CurrentPoint: [ 2x3 double] ColorOrder: [ 7x3 double]. Visible = on 用户可以使用指定的属性名单独查询某一属性,从而获得该属性的具体数据: get(gca,'ColorOrder') 得到的属性为: ans = 0 0 1.0000 0 0.5000 0 1.0000 0 0 0 0.7500 0.7500 0.7500 0 0.7500 0.7500 0.7500 0 0.2500 0.2500 0.2500 如果用户将输出指定的某一变量,则 MATLAB 7.0 将该变量作为一个结构体数组进行赋 值,该数组的域名是对象的属性名,域值是属性名对应的当前取值。例如,如果用户希望绘 制数据 x 和 y,可以执行下面的指令: h = plot(x,y); 还可以通过下面的指令获得由 plot 函数创建的 line 对象的所有属性: a = get(h); 下面用户就可以通过使用 a 的域名来访问 line 属性的数值。以下语句调用 text 函数将字 符串“x and y”作为数据 x 和 y 的起点,文本使用与 line 相匹配的颜色: text(x(1),y(1),'x and y data','Color',a.Color) –443– MATLAB 7.0 如果 x 和 y 是矩阵,plot 函数将为矩阵的每一列绘制一条线。为了标记数据第二列产生 的线条,使用以下语句: text(x(1,2),y(1,2),'Second set of data','Color',a(2).Color) 如果希望查询属性群,可以定义一个属性名单元数组并使用它方便地获取这些属性取 值。例如,假设用户希望查询坐标轴 camera mode 的属性,则定义以下的单元数组: camera_props(1) = {'CameraPositionMode'}; camera_props(2) = {'CameraTargetMode'}; camera_props(3) = {'CameraUpVectorMode'}; camera_props(4) = {'CameraViewAngleMode'}; 使用这个单元数组作为参数来获得这些属性的当前取值: get(gca,camera_props) 运行结果代码如下: ans = 'auto' 'auto' 'auto' 'auto' 12.2.4 设置用户属性默认值 如果用户没有指定属性值,同时又禁止使用默认的属性值,那么 MATLAB 7.0 将使用 系统默认属性值为所有对象定义属性值。用户可以使用以下语句获得所有属性的系统默认 属性值: a = get(0,'Factory'); get 函数将返回一个结构体数组,该数组的域名由对象类型和属性名联合构成,域值是域 名所标识的对象和属性的系统默认值。例如下面的示例代码: UimenuSelectionHighlight: 'on' 标识 uimenu 对象 SelectionHighlight 属性的系统默认属性值为 on。可以使用以下语句获 得单个属性的系统默认属性值: get(0,'FactoryObjectTypePropertyName') 例如下面的示例代码: get(0,'FactoryTextFontName') 对于所有对象的属性,MATLAB 7.0 系统中都对应有一个默认的属性值。 在进行绘图或其他需要了解图像属性值的工作时,MATLAB 7.0 将从当前对象开始,在 继承表中始终向上搜索,直到找到用户定义的默认值或系统,因此总能找到合适的属性值。 可以看出,用户定义的默认值越靠近继承表的 root 对象,MATLAB 7.0 的搜索范围越广。如 果用户在 root 级定义 line 对象的默认值,MATLAB 7.0 将对所有的 line 对象使用这个默认值, 如果用户仅在 axes 级定义 line 对象的默认值,MATLAB 7.0 将对所有的 line 对象使用这个默 认值。如果用户在多级中定义对象的默认值,与该对象最近的父对象级中定义的默认值将被 使用,因为该默认值是 MATLAB 7.0 最先找到的默认值。 这里需要注意的是默认值的设置仅对那些设置完成后所创建的对象有效,已存在的图形 对象不会发生变化。指定默认值时首先要创建一个以 Default 开头、然后紧跟对象类型、最后 是对象属性的字符串。例如,如果希望在当前的图形窗口级指定 line 对象的 LineWidth(线 –444– 第 12 章 高级图形设计 的宽度)属性为 1.5 个点宽,可以使用以下语句: set(gcf,'DefaultLineLineWidth',1.5) 字符串 DefaultLineLineWidth 将指明该属性是 line 对象的 LineWidth 属性。定义图形窗口 的颜色可以使用字符串 DefaultFigureColor。这里要注意的是,仅在 root 级指定图形窗口的属 性才是有意义的。 get(gcf,'default') 使用参数 default 可以将一个属性设置为 MATLAB 7.0 第一个搜索到的默认值。例如,以 下语句将产生一个绿色的曲面 EdgeColor 属性(边缘属性): set(0,'DefaultSurfaceEdgeColor','k') h = surface(peaks); set(gcf,'DefaultSurfaceEdgeColor','g') set(h,'EdgeColor','default') 程序执行结果如图 12-5 所示。 50 45 40 35 30 25 20 15 10 5 0 0 5 10 15 20 25 30 35 40 45 50 图 12-5 设置图形属性的默认值 在 figure 级存在一个该属性的默认值,MATLAB 7.0 将第一个找到这个默认值,于是用 它来替换 root 级定义的默认值。 指定某个属性的数值为“remove”时将会删除用户定义的默认值: set(0,'DefaultSurfaceEdgeColor','remove') 以上语句将 root 级定义的曲面 EdgeColor 默认值删除。 指定一个属性取值为“factory”,则该属性将采用系统默认的属性值。例如,以下语句将 曲面的 EdgeColor 属性设置为系统默认属性值颜色——黑色,而用户设置的默认值将被忽略: set(gcf,'DefaultSurfaceEdgeColor','g') h = surface(peaks); set(h,'EdgeColor','factory') 注意:在使用对象创建函数时,default、remove 和 factory 必须以反斜线开头说明该字符串为保 留字。 –445– MATLAB 7.0 下面给出两个例子来说明以上内容。plot 函数在显示多个图形时将循环使用由坐标轴的 ColorOrder 属性定义的颜色。如果用户为坐标轴的 LineStyleOrder 属性定义多个属性值,那 么 MATLAB 7.0 将在每一次颜色循环后改变线性的宽度。用户还可以通过定义属性默认值使 plot 函数使用不同的线性来创建图形。下面给出几个通过修改属性值进行绘图的例子。 (1)创建一个白底色图形窗口,然后在 root 级为坐标轴对象设置默认值。 %创建一个白底色的窗口 whitebg('w') %设置默认值 set(0,'DefaultAxesColorOrder',[0 0 0],... 'DefaultAxesLineStyleOrder','-|--|:|-.') Z = peaks; plot(1:49,Z(4:7,:)) 无论何时用户调用 plot 函数,该函数将采用同一种颜色绘制所有数据,这是因为坐标轴 的 ColorOrder 属性仅包括一个颜色。但是函数将循环使用 LineStyleOrder 定义的不同线型。 程序运行结果如图 12-6 所示。 图 12-6 root 级设置对象默认值示例 (2)在继承表的多级上设置默认值。 在一个图形窗口中创建两个坐标轴,分别在 figure 级和 axes 级设置坐标轴颜色、线型等 属性的默认值,示例代码设置如下: t = 0:pi/20:2*pi; s = sin(t); c = cos(t); %设置坐标轴颜色属性默认值 figh = figure('Position',[30 100 800 350],... 'DefaultAxesColor',[.8 .8 .8]); axh1 = subplot(1,2,1); grid on –446– 第 12 章 高级图形设计 %在第一个坐标轴处设置线型默认值 set(axh1,'DefaultLineLineStyle','-.') line('XData',t,'YData',s) line('XData',t,'YData',c) text('Position',[3 .4],'String','Sine') text('Position',[2 -.3],'String','Cosine',... 'HorizontalAlignment','right') axh2 = subplot(1,2,2); grid on %在第二个坐标轴处设置文本旋转属性的默认值 set(axh2,'DefaultTextRotation',90) line('XData',t,'YData',s) line('XData',t,'YData',c) text('Position',[3 .4],'String','Sine') text('Position',[2 -.3],'String','Cosine',... 'HorizontalAlignment','right') 此时,不同的子图形使用相同的 text 和 line 函数会得到不同的显示结果,如图 12-7 所示。 1 1 0.8 0.8 0.6 0.6 Sine 0.4 Sine 0.4 0.2 0.2 0 0 Cosine -0.2 -0.2 Cosine -0.4 -0.4 -0.6 -0.6 -0.8 -0.8 -1 0 2 4 6 8 -1 0 2 4 6 8 图 12-7 使用不同默认值属性值的不同结果 由于默认的坐标颜色(Color)属性是在图形窗口级(figure)设置的,所以 MATLAB 7.0 使用相同的灰色背景颜色创建坐标轴。作图的坐标轴点划线是默认线型,在该图中每一次调 用 line 函数都使用这种线型,而右边的坐标轴没有定义默认线型,所以 MATLAB 7.0 使用系 统默认属性值——实线型。右边的坐标轴给文本的 Rotation 属性定义为 900。该图中所有的 文本都使用这种旋转角度,而左边的坐标轴给文本使用固有默认值,不旋转文本。 如果用户希望无论何时运行 MATLAB 7.0 都使用用户定义的默认值,可以在 startup.m 文 件中指定这些默认值。 注意:在调用 colordef 命令时,MATLAB 7.0 可能会自动装载一些外观属性的系统默认值。 –447– MATLAB 7.0 12.3 句柄使用方法 12.3.1 访问对象句柄 MATLAB 7.0 给所创建的每一个图形对象指定一个句柄。所有的对象创建函数都能够返 回被创建对象的句柄。如果用户希望访问对象的属性,那么最好在创建对象时将对象的句柄 赋给一个变量,以免以后对句柄进行重复搜索。当然,用户也可以使用 findobj 函数或通过查 询其父对象的 Children 属性获得已存在对象的句柄。 除了 root 对象和 figure 对象以外,所有图形对象的句柄都是一个浮点数。用户使用这些 句柄数据时必须保证这些数据的全部精度。root 对象的句柄总是 0;figure 对象的句柄可以是 显示在窗口标题栏中的整数(默认情况下),也可以是一个完全符合 MATLAB 7.0 内部浮点 数精度的数值。 图像对象的所有操作都是针对当前对象而言的。“当前”是图形对象的一个重要概念, 当前窗口是指被指定作为图形输出的窗口;当前坐标轴是创建坐标轴子对象命令的目的坐标 轴;当前对象是用户创建的最后一个图形对象或鼠标单击的最后一个对象。 MATLAB 7.0 分别将这些当前对象的句柄保存在其父对象的属性中,如图 12-8 所示。 图 12-8 当前对象句柄保存途径 从图 12-8 中列出的属性可以获得这些关键对象的句柄,代码设置如下: get(0,'CurrentFigure'); get(gcf,'CurrentAxes'); get(gcf,'CurrentObject'); 以上语句中的 gcf、gca、gco 是 get 函数的速记符,它们的含义如下: • gcf:返回 root 的 CurrentFigure 属性值; • gca:返回当前图形窗口的 CurrentAxes 属性值; • gco:返回当前图形窗口的 CurrentObject 属性值。 用户可以使用这些速记符(也可以称为命令)作为函数的对象句柄参数。例如,用户可 以选择一个 line 对象,然后使用 gco 作为 set 函数的参数来指定该 line 对象的句柄: set(gco,'Marker','square') 或者使用以下语句列举出所有当前坐标轴的属性值: get(gca) 下面的语句可以获得当前坐标轴中的图形对象的句柄: –448– 第 12 章 高级图形设计 h = get(gca,'Children'); 通过以下语句确定对象的类型: get(h,'type') 最后执行结果的代码如下: ans = 'text' 'patch' 'surface' 'line' 虽然 gcf 和 gca 提供了一个简单地获取当前窗口和坐标轴句柄的方法,但是很少在 M 文 件中使用这两个命令,因为一般设计 MATLAB 7.0 程序 M 文件时,不会根据用户行为来获得 当前对象。 MATLAB 7.0 还提供了一种通过属性搜索对象的方法,即 findobj 函数。findobj 函数能够 快速地形成一个继承表的横截面并获得具有指定属性值的对象句柄。如果用户没有指定一个 开始搜索的对象,findobj 函数将从 root 对象开始,始终搜索与用户指定属性名和属性值相符 的所有事件。下面举例说明该函数的使用方法。 在如图 12-9 所示的 sin 函数图形中包括用来标注特殊点的文本对象。 图 12-9 含有文本对象的图形 假设用户希望将文本字符串 sin(t)= 0.707 从当前位置移动到[3*pi/4,sin(3*pi/4)]。首先 要获得文本对象的句柄,然后通过使用这个句柄修改文本对象的属性 Position。 为了使用函数 findobj,选择惟一一个能够表示该文本对象的属性值来搜索当前对象的句 柄。在本例中使用文本对象的 String 属性值。 text_handle = findobj('String','\leftarrowsin(t) = .707'); 然后重新定义文本的 Position 属性值,将该对象移动到新的位置: set(text_handle,'Position',[3*pi/4,sin(3*pi/4),0]) –449– MATLAB 7.0 findobj 函数也允许用户通过指定多起始点来限制搜索,当继承表中有许多对象时,这样 做将会大大提高搜索的速度。在本例中,假设用户知道所需的文本对象位于当前的坐标轴中, 可以用以下语句进行快速搜索: text_handle = findobj(gca,'String','\leftarrowsin(t) = .707'); 12.3.2 使用句柄操作图形对象 1.对象拷贝 用户可以使用 copyobj 函数将一个对象从一个父对象拷贝到另一个父对象中。新对象与 旧对象惟一的不同就是其 Parent 属性和句柄。可以同时将多个对象拷贝到一个新的父对象中, 也可以将一个对象拷贝到多个父对象中而不改变当前的父子关系。当用户拷贝一个有子对象 的对象时,MATLAB 7.0 同时也拷贝其所有的子对象。 假设用户正在绘制多个数据并且希望标注每个图形点(5π/4,sin(5π/4))。text 函数将使 用字符串{5π/4,sin(5π/4)}和一个右箭头来标注数据点,并将'HorizontalAlignment'属性修改 为 right 使得数据点位于文本字符串的右边。 text('String','\{5\pi\div4, sin(5\pi\div4)\}\rightarrow',... 'Position',[5*pi/4,sin(5*pi/4),0],... 'HorizontalAlignment','right') 得到的结果如图 12-10 所示。 图 12-10 待拷贝的图形 为了使用同样的字符串对另外一个点进行标注,首先使用 copyobj 函数拷贝文本对象。 由于以上的语句并没有保存文本对象的句柄,所以用户必须通过 findobj 函数和 String 属性获 得句柄,代码设置如下: text_handle = findobj('String',... '\{5\pi\div4,sin(5\pi\div4)\}\rightarrow'); 在创建第二个图形后,通过第一个图形中拷贝标签来为第二个图形添加标签: copyobj(text_handle,gca) –450– 2.对象删除 第 12 章 高级图形设计 用户可以使用 delete 命令和句柄参数删除一个图形对象。例如,用户可以使用以下语句 删除当前的坐标轴(以及其所有子对象): delete(gca) 也可以通过 findobj 命令来获得需要删除的特定对象的句柄。 例如,假设希望删除图 12-10 中的点线,首先获得点线的句柄: line_handle = findobj('LineStyle',':'); 然后使用这个句柄删除由上述语句获得的线: delete(line_handle) 图 12-11 待删除的点线图形 如果将以上两个语句并为一个,代码设置如下: delete(findobj('LineStyle',':')) 删除结果如图 12-12 所示。 图 12-12 利用句柄删除点线后的图形 –451– MATLAB 7.0 12.3.3 控制图形输出 MATLAB 7.0 允许在同一次运行过程中打开多个图形窗口,所以当一个 MATLAB 7.0 程 序将创建图形窗口来显示图形用户界面并绘制数据时,有必要对某些图形窗口进行保护,以 免成为图形输出的目标,而相应的输出窗口要做好接受新图形的准备。以下内容将讨论如何 使用句柄来控制 MATLAB 7.0 显示图形的目标和方法。 1.指定输出目标 默认情况下,MATLAB 7.0 图形创建函数在当前的图形窗口和坐标轴中显示图形。用户 可以通过在图形创建函数中使用明确的 Parent 属性直接指定图形的输出位置。例如: plot(1:10,'Parent',axes_handle) 在上述代码中,axes_handle 是目的坐标轴的句柄。 uicontrol 和 uimenu 函数指定 Parents 属性的语法比较简单: uicontrol(Figure_handle,...) uimenu(parent_menu_handle,...) 默认情况下,产生图形输出的函数将在当前的图形窗口中显示该图形而不擦除或重置当 前窗口的属性。然而,如果图形对象是坐标轴的子对象,为了显示这些图形将会擦除坐标轴 并重置坐标轴的大多数属性。用户可以通过设置图形窗口和坐标轴的 NextPlot 属性来改变 MATLAB 7.0 的这种行为。MATLAB 7.0 高级图形函数在绘制图形前先要检查 NextPlot 属性, 然后来决定是添加还是擦除重置图形和坐标轴。而低级对象创建函数则不检查 NextPlot 属性, 只是简单地在当前窗口和坐标轴中添加新地图形对象。 在表 12-2 中列出了 NextPlot 属性的可能取值。 表 12-2 NextPlot 属性地可能取值 NextPlot Add 图形窗口 坐标轴 添加新的图形而不擦除或重置当前 添加新的图形而不擦除或重置 窗口 当前坐标轴 Replacechildren Replace 删除所有子对象但不重置窗口属 性,等同于 clf 函数 删除所有子对象并将窗口重置为默 认属性,等同于 clf 函数 删除所有子对象但不重置坐标 轴属性,等同于 cla 函数 删除所有子对象并将坐标轴重 置为默认属性,等同于 cla 函数 hold 命令提供访问 NextPlot 属性的简便方法,以下语句将图形和坐标轴的 NextPlot 属性 都设置为 add: hold on 以下语句将图形和坐标轴的 NextPlot 属性都设置为 replace: hold off MATLAB 7.0 提供 newplot 函数来简化图形 M 文件设置 NextPlot 属性的编写过程。newplot 函数首先检查 NextPlot 属性值,然后根据属性值决定相应的行为。用户应该在所有调用图形 创建函数的 M 文件的开头定义 newplot 函数。当用户调用 newplot 函数时,有可能发生以下 行为。 (1)检查当前图形窗口的 NextPlot 属性 • 如果不存在图形窗口,则创建一个窗口并设该窗口为当前窗口; –452– 第 12 章 高级图形设计 • 如果 NextPlot 值为 add,将该窗口设置为当前窗口; • 如果 NextPlot 值为 replacechildren,删除窗口的子对象并设置该窗口为当前窗口; • 如果 NextPlot 值为 replace,删除窗口的子对象,重置窗口属性为默认值,并设置该窗 口为当前窗口。 (2)检查当前坐标轴的 NextPlot 属性 • 如果不存在坐标轴,则创建一个坐标轴并设该坐标轴为当前坐标轴; • 如果 NextPlot 值为 add,将该坐标轴设置为当前坐标轴; • 如果 NextPlot 值为 replacechildren,删除坐标轴的子对象,并设置该坐标轴为当前坐标 轴; • 如果 NextPlot 值为 replace,删除坐标轴的子对象,重置坐标轴属性为默认值,并设置 该坐标轴为当前坐标轴。 默认情况下,图形窗口的 NextPlot 值为 add,坐标轴的 NextPlot 值为 replace。 为了说明 newplot 的使用方法,下面给出一个类似与 plot 的绘图函数 my_plot,该函数在 绘制多个图形时将循环使用不同的线型,而不是使用不同的颜色,具体代码设置如下: function my_plot(x,y) % newplot 返回当前坐标轴的句柄 cax = newplot; LSO = ['- ';'--';': ';'-.']; set(cax,'FontName','Times','FontAngle','italic') set(get(cax,'Parent'),'MenuBar','none') line_handles = line(x,y,'Color','b'); style = 1; for i = 1:length(line_handles) if style > length(LSO), style = 1;end set(line_handles(i),'LineStyle',LSO(style,:)) style = style + 1; end grid on 函数 my_plot 使用低级函数 line 语法来绘制数据,虽然 line 函数并不检查图形窗口和坐 标轴的 NextPlot 属性值,但是 newplot 的调用使得函数 my_plot 与高级函数 plot 执行相同的 操作,即每一次用户调用该函数时,函数都对坐标轴进行清除和重置。my_plot 函数是使用 newplot 函数返回的句柄来访问图形窗口和坐标轴。该函数还设置了坐标轴的字体属性并禁止 使用图形窗口的菜单。调用 my_plot 函数的绘图结果如图 12-13 所示。 my_plot(1:10,peaks(10)) –453– MATLAB 7.0 图 12-13 my_plot 函数的典型输出结果 2.保护图形和坐标轴 有些情况下需要对图形窗口和坐标轴进行保护以免其成为图形输出的目标。用户可以将 特定窗口或坐标轴的句柄从句柄列表中删除,使 newplot 和其他返回或参考句柄的函数(gca, gco,cla,clf,close 和 findobj)无法找到该句柄,从而保证该窗口不会成为其他程序的输出 目标。HandleVisibility 和 ShowHiddenHandles 两个属性可以用来控制保护对象句柄的可见与 否。 HandleVisibility 是一个所有对象都具备的属性,该属性值可以为以下取值: • on:对象句柄可以被任意函数获得,这是该属性的默认值; • callback:对象句柄对所有在命令行中执行的函数隐藏,而对所有回调函数(用户行为 处理函数)总是可见的。这种可见程度将保证用户在命令行中键入的命令不会影响被 保护对象; • off:句柄对所有函数,无论是命令行中执行的还是回调的函数,都是隐藏的。 例如,如果一个用户图形界面以文本字符串形式接受用户的输入,并在回调函数中对这 个字符串进行处理,那么如果不对窗口进行保护,字符串“close all”有可能会导致用户图形 界面的销毁。为了防止这种情况,用户可以将该窗口关键对象的 HandleVisibility 数值暂时设 置为 off: user_input = get(editbox_handle,'String'); set(gui_handles,'HandleVisibility','off') eval(user_input) set(gui_handles,'HandleVisibility','commandline') 如果一个被保护的图形窗口是屏幕最顶层的窗口,而且在它之下存在未保护的窗口,那 么使用 gcf 将返回最高层的未被保护的窗口,gca 的情况与之相同。如果不存在未保护的窗口 或坐标轴,MATLAB 7.0 将创建一个窗口并返回它的句柄。 root 对象的 ShowHiddenHandles 属性是句柄可视的使能控制属性。ShowHiddenHandles –454– 第 12 章 高级图形设计 的默认值为 off。当该属性值为 on 时,句柄对所有函数都是可视的。当用户希望访问某一特 定时刻存在的所有对象时,该属性将十分有用。这里需要说明的一点是,close 函数可以通过 使用 hidden 选项来访问不可见的窗口,例如下面的示例代码: close('hidden') 即使该窗口是被保护的,该语句也将关闭屏幕最顶端的窗口。使用以下语句将关闭所有 窗口: close('all','hidden') 有些情况下需要改变坐标轴的可视外观来适应新的图形对象。为了与 MATLAB 7.0 的高 级函数保持一致,在改变坐标轴和图形窗口之前,最好先测试一下 hold 属性是否为 on,当 hold 属性为 on 时,坐标轴和图形窗口的 NextPlot 数值均为 add。以下的 my_plot3 函数将接 收三维数据并使用 ishold 来检查 hole 属性的状态,以此来决定是否更改视图。 function my_plot3(x,y,z) cax = newplot; %检测当前的 hold 状态 hold_state = ishold; LSO = ['- ';'--';': ';'-.']; if nargin == 2 hlines = line(x,y,'Color','k'); %如果 hold 为 off 时,改变视图 if ~hold_state view(2) end elseif nargin == 3 hlines = line(x,y,z,'Color','k'); %如果 hold 为 off 时,改变视图 if ~hold_state view(3) end end ls = 1; for hindex = 1:length(hlines) if ls > length(LSO),ls = 1;end set(hlines(hindex),'LineStyle',LSO(ls,:)) ls = ls + 1; end 如果 hold 属性为 on,调用 my_plot3 时将不改变视图,否则有 3 个输入参数, MATLAB 7.0 将视图由二维变为三维。 3.关闭请求 定义 CloseRequestFcn 属性使用户能够组织或延迟图形窗口的关闭以及 MATLAB 7.0 运 –455– MATLAB 7.0 行的终止。发生以下几种情况时,MATLAB 7.0 将执行由图形窗口的 CloseRequestFcn 属性定 义的回调函数(称为关闭请求函数): • 在图形窗口中调用 close 命令; • 用户退出 MATLAB 7.0 时还存在可见的图形窗口(如果一个窗口的 Visible 属性值为 off,则退出 MATLAB 7.0 时并不执行关闭请求函数,而是删除该图形窗口); • 使用窗口系统的关闭菜单或按钮来关闭图形窗口。 默认的关闭请求函数保存在一个名为 closereq.m 的 M 文件中,该函数包括以下语句: shh=get(0,'ShowHiddenHandles'); set(0,'ShowHiddenHandles','on'); delete(get(0,'CurrentFigure')); set(0,'ShowHiddenHandles',shh); 这个关闭请求函数通过设置 root 对象的 ShowHiddenHandles 属性使得 HandleVisibility 属 性的设置无效,即将所有图形窗口的句柄变成可见。这样,当用户退出 MATLAB 7.0 时,当 前图形窗口的关闭请求函数将被调用,在关闭请求函数中删除该图形窗口,然后 root 对象 children 属性列表中的下一个窗口将变成当前窗口,执行它的关闭请求函数,如此重复该过 程直至所有的窗口都被删除。此时,如果 MATLAB 7.0 不删除图形窗口, MATLAB 7.0 不能 退出。 如果在视图关闭窗口时关闭请求函数发生错误,那么 MATLAB 7.0 将终止退出操作。但 是如果使用 quit 或 exit 命令退出 MATLAB 7.0,即使关闭请求函数发生错误,MATLAB 7.0 也会无条件地关闭窗口。delete 函数也总是无视 CloseRequestFcn 属性的取值而无条件地关闭 指定的窗口,例如下面的示例代码: delete(get(0,'Children')) 上面的语句将删除所有句柄可见的窗口(即所有 HandleVisibility 属性为 on 的窗口)。如 果 用 户 希 望 删 除 所 有 窗 口 ( 不 论 句 柄 是 否 隐 藏 ), 那 么 可 以 通 过 设 置 root 对 象 的 ShowHiddenHandles 属性为 on 来实现。例如下面的语句: set(0,'ShowHiddenHandles','yes') delete(get(0,'Children')) 将无条件地删除所有图形窗口。 12.3.4 在 M 文件中保存句柄 所有句柄无论可见与否都能够保持其有效性,如果用户确实知道某对象的句柄,无论该 句柄可见与否,用户都可以使用句柄进行对象属性的设置或查询。 图形 M 文件经常使用句柄来访问属性值,并通过句柄直接定义图形输出的目标。 MATLAB 7.0 提供一些有用的函数来获得图形关键对象(例如当前窗口和坐标轴)的句柄, 然而在 M 文件中,使用这些函数并不是获得句柄最好的方法。这是因为,在 MATLAB 7.0 中查询对象句柄或其他信息的执行效率并不高,最好还是将句柄直接保存在一个变量中进行 引用。另外,由于当前的坐标轴、图形窗口或对象有可能因为用户的交互而发生变化,查询 方式难以确保句柄完全正确,而使用句柄变量则可以保证正确地反映对象发生的变化。 为了保存句柄信息,通常用户在 M 文件的开始处保存 MATLAB 7.0 相关的状态信息。 –456– 第 12 章 高级图形设计 例如,用户可以使用以下语句作为 M 文件的开头: cax = newplot; cfig = get(cax,'Parent'); hold_state = ishold; 这样就无需在每次需要这些信息时都重新进行查询。如果用户在 M 文件中暂时改变了保 持状态,用户应当将 NextPlot 的当前属性值保存下来,以便以后重新设置。 ax_nextplot = lower(get(cax,'NextPlot')); fig_nextplot = lower(get(cfig,'NextPlot')); set(cax,'NextPlot',ax_nextplot) set(cfig,'NextPlot',fig_nextplot) 有一些内置的函数可以修改坐标轴的属性以实现某种特定的效果,这些函数可能会对用 户的 M 文件产生一定的影响。 表 12-3 列出了一些 MATLAB 7.0 的内置函数以及它们所修改的属性。注意这些属性仅在 hold 设置为 off 时才会发生变化。 表 12-3 函数名 fill Fill3 image (high-level) loglog 某些 MATLAB 7.0 的内置函数修改的属性 坐标轴属性(改变后) 函数名 Box: on CameraPosition: 2-D view CameraTarget: 2-D view CameraUpVector: 2-D view CameraViewAngle: 2-D view CameraPosition: 3-D view CameraTarget: 3-D view CameraUpVector: 3-D view CameraViewAngle: 3-D view XScale: linear YScale: linear ZScale: linear Box: on Layer: top CameraPosition: 2-D view CameraTarget: 2-D view CameraUpVector: 2-D view CameraViewAngle: 2-D view XDir: normal XLim: [0 size(CData,1)]+0.5 XLimMode: manual YDir: reverse YLim: [0 size(CData,2)]+0.5 YLimMode: manual Box: on CameraPosition: 2-D view CameraTarget: 2-D view CameraUpVector: 2-D view CameraViewAngle: 2-D view XScale: log YScale: log plot plot3 semilogx Semilogy 坐标轴属性(改变后) Box: on CameraPosition: 2-D view CameraTarget: 2-D view CameraUpVector: 2-D view CameraViewAngle: 2-D view CameraPosition: 3-D view CameraTarget: 3-D view CameraUpVector: 3-D view CameraViewAngle: 3-D view XScale: linear YScale: linear ZScale: linear Box: on CameraPosition: 2-D view CameraTarget: 2-D view CameraUpVector: 2-D view CameraViewAngle: 2-D view XScale: log YScale: linear Box: on CameraPosition: 2-D view CameraTarget: 2-D view CameraUpVector: 2-D view CameraViewAngle: 2-D view XScale: linear YScale: log –457– MATLAB 7.0 12.4 GUI 设计向导 一个可发布的应用程序通常都需要具备一个友好的图形界面,本章就来介绍创建图形用 户界面(GUI)的具体方法。本章首先对 GUI 的基本概念作简单的介绍,然后说明 GUI 开发 环境 GUIDE 及其组成部分的用途和使用方法,最后说明创建一个完整 GUI 的详细步骤。 12.4.1 图形用户界面概述 通常在开发一个实际的应用程序时都会尽量做到界面友好,最为常用的方法就是使用图 形界面。提供图形用户界面的应用程序能够使用户的学习和使用更为方便容易。用户不需要 知道应用程序究竟怎样执行各种命令的,而只需要了解可见界面组件的使用方法;用户也不 需要知道命令是怎样执行的,只要通过与界面交互就可以使指定的行为得以正确执行。 在 MATLAB 7.0 中,图形用户界面是一种包含多种对象的图形窗口。用户必须对每一个 对象进行界面布局和编程,从而使用户在激活 GUI 每个对象时都能够执行相应的行为。另外, 用户必须保存和发布所创建的 GUI,使得 GUI 能够真正地得到应用。 MATLAB 为用户开发图形界面提供了一个方便高效的集成开发环境,MATLAB 图形用 户界面开发环境 GUIDE(MATLAB Graphical User Interface Development Environment)。上述 所有工作都能够使用 GUIDE 方便地实现。GUIDE 主要是一个界面设计工具集,MATLAB 7.0 将所有 GUI 支持的用户控件都集成在这个环境中并提供界面外观、属性和行为响应方式的设 置方法。GUIDE 将用户保存设计好的 GUI 界面保存在一个 FIG 资源文件中,同时还能够生 成包含 GUI 初始化和组件界面布局控制代码的 M 文件。这个 M 文件为实现回调函数(当用 户激活 GUI 某一组件时执行的函数)提供了一个参考框架。虽然使用户自己编写的包含 GUI 所有发布命令的 M 文件也能够实现一个 GUI,但是使用 GUIDE 执行效率更高。使用 GUIDE 不但能够交互式地进行组件界面布局,而且能够生成两个用来保存和发布 GUI 的文件。 • FIG 文件:该文件包括 GUI 图形窗口及其所有后裔的完全描述,包括所有对象的属性 值。FIG 文件式一个二进制文件,调用 hgsave 命令或界面设计编辑器的 File 菜单 Save 选项保存图形窗口时将生成该文件。FIG 文件包含序列化的图形窗口对象,在用户打 开 GUI 时,MATLAB 7.0 能够通过读取 FIG 文件重新构造图形窗口及其所有后裔。所 有对象的属性都被设置为图形窗口创建时保存的属性。默认情况下,即使用户使用 hgsave 和 hgload 命令保存系统默认的图形工具条和菜单,FIG 文件也不保存这些默认 信息。FIG 文件的作用之一就是对象句柄的保存和引用。可以使用 open、openfig 和 hgload 命令类来打开一个后缀为.fig 的文件; • M 文件:该文件包括 GUI 设计、控制函数以及定义为子函数的用户控件回调函数,主 要用于控制 GUI 展开时的各种特征。该 M 文件可分为 GUI 初始化和回调函数两个部 分,用户控件的回调函数根据用户与 GUI 的具体交互行为分别调用。这里将 GUI 的 M 文件称为应用程序 M 文件。应用程序 M 文件使用 openfig 命令来显示 GUI。注意应 用程序 M 文件并不包括用户界面设计的任何代码,这些代码将完全由 FIG 文件保存。 GUIDE 可以根据用户 GUI 的版面设计过程直接自动生成 M 文件框架,这样就简化了 GUI 应用程序的创建工作,用户可以直接使用这个框架来编写自己的函数代码。这样的编写 –458– 第 12 章 高级图形设计 方法具有以下优点: • 应用程序 M 文件已经包含实现一些有用的函数代码,无需用户自行编写; • 可以使用该 M 文件生成的有效方法来管理图形对象句柄并执行回调函数子程序; • 提供管理全局数据的途径; • 文件支持自动插入回调函数原型,确保当前 GUI 与未来发布版本的兼容性。 用户也可以选择由 GUIDE 生成 FIG 文件、自己编写应用程序 M 文件的 GUI 创建方式。 编写 M 文件时要注意,应用程序 M 文件中不能包含用户控件的创建命令,所有的界面设计 信息都保存在由界面设计编辑器生成的 FIG 文件中。 实现一个 GUI 主要包括 GUI 界面设计和 GUI 组件编程两项工作。整个 GUI 的实现过程 可以分为以下几步: • 通过设置 GUIDE 应用程序的选项来进行 GUIDE 组态; • 使用界面设计编辑器进行 GUI 界面设计; • 理解应用程序 M 文件中所使用的编程技术; • 编写用户 GUI 组件行为响应控制(即回调函数)代码。 12.4.2 启动 GUIDE 在 MATLAB 7.0 中,GUIDE 提供了多种设计模板以方便用户轻松地定制属于自己的 GUI。这些模板均包括了相关的回调函数,用户可以打开它所对应的 M 文件,看到它们的工 作方式,或修改相应的函数,从而实现自己需要的功能。 在 MATLAB 7.0 中,可以通过如下两种方法来访问模板: • 直接输入 GUIDE 命令,打开如图 12-14 所示的界面; • 如果 GUIDE 已经打开,通过 File 菜单中的 New 选项也可以打开 GUI 模板的设置界面。 图 12-14 GUI 模板设置界面 在模板设计界面中,用户可以选择创建新的 GUI 或者打开原有的 GUI。在创建新的 GUI 中,MATLAB 7.0 提供了空白模板、带有控制按钮的模板、带有坐标轴和菜单的模板以及问 答式对话框 4 种模板。其中空白模板如图 12-15 所示。 –459– MATLAB 7.0 图 12-15 空白界面设计编辑器外观 12.4.3 GUIDE 提供的用户控件 在空白模板中 GUIDE 提供了用户界面控件以及界面设计工具集来实现用户界面的创建 工作。用户界面控件分布在界面设计编辑器的左侧,如图 12-16 所示。 图 12-16 各种控件图示 下面简单介绍各种控件的概念和特点。 • 按钮:通过鼠标单击按钮可以实现某种行为(按钮下陷和弹起等)并调用相应的回调 子函数; • 滚动条:使用户能够通过移动滚动条来改变指定范围内的数值输入,滚动条的位置代 表用户输入的数值; • 单选按钮:单选按钮与按钮的执行方式没有本质上的区别,但是单选按钮通常以组为 –460– 第 12 章 高级图形设计 单位,一组单选按钮之间是一种互相排斥的关系,也就是说任何时候一组单选按钮中 只能有一个有效; • 复选框:复选框与单选按钮类似,只是多个复选框可以同时有效。复选框为用户提供 一些可以独立选择的选项进行设置程序模式,例如显示工具条与否以及生成回调函数 原型与否等; • 编辑框:编辑框是控制用户编辑或修改字符串的文本域,其 String 属性包含用户输入 的文本信息。如果一个编辑框有输入焦点,在 UNIX 系统中,单击图形窗口的菜单栏, 编辑框回调函数就会被调用,而在 Windows 系统下,单击图形窗口不会导致编辑框回 调函数的执行。这个区别是处于操作平台方便性考虑而定的; • 静态文本:静态文本通常作为其他控件的标签使用,用户不能采用交互方式修改静态 文本或调用响应的回调函数; • 弹出式菜单:弹出式菜单将打开并显示一个由其 String 属性定义的选项列表。当用户 希望提供一些相互排斥的选项,但不希望使用一系列占用有限空间的单选按钮时,文 本菜单将非常有用; • 列表框:列表框显示由其 String 属性定义的一系列列表项,并使用户能够选择其中的 一项或多项; • 拴牢按钮:拴牢能够产生一个二进制状态的行动(on 或 off)。单击该按钮将使按钮的 外观保持下陷状态,同时调用响应的回调函数。再次单击该按钮将使按钮弹起,同时 也要调用回调函数。拴牢按钮的回调函数首先要对按钮的状态进行查询,然后才能决 定相应的行为; • 坐标轴:坐标轴使用户的 GUI 可以显示图片,像所有的图像对象一样,坐标轴可以设 置许多关于外观和行为的参数; • 组合框:组合框是图形窗口中的一个封闭区域,它把相关联的控件(例如一组单选按 钮)组合在一起,使得用户界面更容易理解;组合框可以有自己的标题以及各种边框; • 按钮组:按钮组类似于组合框,但是它可以响应关于单选按钮以及拴牢按钮的高级属 性,例如单选按钮之间的互斥属性。 注意:后边两种为 MATLAB 7.0 中新增的控件。 12.4.4 界面设计工具集 GUIDE 提供了一个界面设计工具集来实现图形用户界面的创建工作,这些界面设计工具 包括: • 界面设计编辑器:添加并排列图形窗口中的组件对象; • 属性检查器:检查并设置组件的属性值; • 图像浏览器:观察此次 MATLAB 7.0 运行过程中图形对象的句柄集成关系表; • 菜单编辑器:创建窗口菜单和文本菜单。 1.界面设计编辑器 界面设计编辑器使用户能够从组件面板种选择 GUI 组件并将它们排列在图形窗口中。界 –461– MATLAB 7.0 面设计编辑器由组件面板、工具栏、菜单栏和界面区域 4 个部分组成。组件面板包含用户界 面可获得的所有组件(用户控件对象);工具栏和菜单栏可以用来启动其他界面设计工具,例 如菜单编辑器;界面区域实际上就是激活后的 GUI 图形窗口。 (1)组件面板 在 GUI 界面中放置组件,首先单击组件面板重的按钮,选择需要放置的组件类型,光标 变为十字形后使用十字形光标的中心点来确定组件左上角的位置,或者通过在界面区域内单 击并拖动鼠标来确定组件的大小。图 12-17 给出了一个添加组件的示例。 GUI 组件布置工作完成后,用户可以使用激活按钮或选择 Tools 菜单的 ActivateFigure 选 项来观察 GUIDE 的设计结果。激活图形窗口将发生以下事件:首先保存 FIG 文件和 M 文件, 如果用户尚未保存该设计结果,GUIDE 将打开“保存:对话框使用户选择将要创建的 M 文 件名;然后 GUIDE 保存与 M 文件同名的 FIG 文件(扩展名为.fig),如果存在一个同名的 M 文件,GUIDE 将会显示一个提示对话框,如果用户选择提示对话框中的 Replace 按钮,原来 的 M 文件将被替换;如果选择:Append”,则 GUIDE 将向原有的 M 文件中插入未保存的新 组件回调函数并根据应用程序选项对话框设置的变化来修改原有代码。 图 12-17 在界面中添加组件的示例 注意:如果 FIG 文件和 M 文件已经存在,那么当用户激活 GUI 时,GUIDE 将自动保存应用程序 M 文件和 FIG 文件,因此不要随意激活 GUI 以免有用的组件被替换。 (2)文本菜单 使用界面设计编辑器进行界面设计时,可以先选择一个对象,然后单击鼠标右键来显示 与所选对象相连的文本菜单。图 12-18 表示了一个与图形窗口对象联系在一起的文本菜单, 所有已定义的回调子函数都列举在该菜单的下方。 –462– 第 12 章 高级图形设计 图 12-18 图形窗口的文本菜单 图 12-19 描述了一个与按钮相联系的文本菜单,同样,所有定义的回调函数都列举在该 菜单的下方。 图 12-19 GUI 用户控制文本菜单 (3)排列工具 用户可以在界面区域内通过选择并拖动任意组件或组件群进行组件排列。另外,界面设 计编辑器还提供一些更为精确的组件排列方法。 • 排列工具:排列并分布组件群; • 栅格和标尺:指定任意位置的水平和竖直标线; • 指引线:指定任意位置的水平和竖直标线; • 拉前推后:控制组件的前后顺序。 –463– MATLAB 7.0 排列工具使用户能够根据其他组件的位置调整被选择组件的间隔或放置某个组件。上述 的排列方法可以通过排列工具栏获得,当用户按下“Apply”按钮时,指定的排列操作将应用 于所有被选择的组件。图 12-20 显示了排列工具栏的外观。 图 12-20 排列工具栏外观 (4)网线和标线 界面区域可以使用网格和标线辅助完成组件设计工作。用户可以将网格线的间隔设置在 10~200 个像素之间,默认情况下以 50 个像素为间隔。如果用户选择了“snap-to-grip”选项, 那么对于任何一个在网格线周围 9 像素范围内移动或重画的对象,系统都会自动将该对象放 置在该网格线上。无论网格是否可见,snap-to-grip 选项都是有效的。 网格和标线对话框外观如图 12-21 所示。 图 12-21 网格和标线对话框外观 该对话框可以完成以下工作。 • 控制标线、网格和指引线的可见与否; • 设置网格间距; • 使能 snap-to-grip。 界面设置编辑器有水平和竖直两种指引线。当用户希望在界面设计编辑器的任意位置建 立一个组件排列参考标准时,指引线将非常有用。单击标线的左边或顶端并将其拖放到界面 –464– 区域中所需位置处就会生成一条指引线。 第 12 章 高级图形设计 2.属性检查器 属性检查器提供一个所有可设置属性的列表并显示当前的属性值,使用户能够设置界面 中各组件的属性。列表中的每个属性都对应于一个相应于该属性的属性值选择范围。属性检 查器的外观如图 12-22 所示。 图 12-22 属性检查器外观 用户可以通过多种方式来启动属性检查器。双击界面设计编辑器中的组件;选择 Tools 菜单下的“Property Inspector”选项;选择 Edit 菜单下的“Inspect Property”选项;从组件的 文本菜单中选择“Property Inspector”选项。 通过对 GUI 中各个用户控制对象的属性设置可以实现用户所需的控件外观和行为特征。 3.对象浏览器 对象浏览器显示图形窗口中所有对象的继承关系。图 12-23 所为对象浏览器的示例,从 图中可以看出,第一个创建的是用户控件组合框,然后是 4 个单选按钮,最后是坐标轴。 图 12-23 对象浏览器示例 4.菜单编辑器 菜单编辑器的外观如图 12-24 所示。 –465– MATLAB 7.0 图 12-24 菜单编辑器外观 GUIDE 能够创建两种类型的菜单。在图形窗口菜单栏中显示的菜单栏菜单;当用户在图 形对象上单击鼠标右键时弹出的文本菜单。可以使用菜单编辑器来创建这两种类型的菜单。 (1)定义菜单栏菜单 创建菜单的第一步是使用 New Menu 工具栏创建一个菜单,然后来指定菜单的属性。用 户单击创建的菜单项将会显示如图 12-25 所示的一个文本域,在该文本域中可以设置菜单的 标签、分隔符、选中模式以及回调函数字符串。 图 12-25 菜单的相关设置 菜单创建成功后,MATLAB 7.0 将该菜单添加到图形窗口的菜单中。 下面将创建菜单项。使用 New Menu Item 工具来添加菜单项,每一个菜单项也可以有级 联的子菜单项。图 12-26 显示了为窗口菜单栏定义了 3 个菜单的菜单编辑器外观。 –466– 第 12 章 高级图形设计 图 12-26 菜单项创建示例 如果用户激活图形窗口,这 3 个菜单将会出现在窗口中,如图 12-27 所示。 图 12-27 菜单创建结果 (2)定义文本菜单 定义了文本菜单的对象后,当用户单击鼠标右键时,文本菜单随之出现。菜单编辑器能 够定义文本菜单并将菜单与对象联系起来。 文本菜单的所有菜单项都是文本菜单的子对象,这些菜单项并不显示在窗口菜单栏中。 选择菜单编辑器工具条中的“New Context Menu”来创建父菜单并为之定义一个标签(名称)。 注意在定义文本菜单之前要选择菜单编辑器的 Context Menus 标签界面。使用菜单编辑器工 具条中的“New Menu Item”按钮来创建文本菜单项,然后给该菜单项添加一个标签并定义 回调字符串。 在界面编辑器中选择需要定义文本菜单的对象,使用属性检查器将该对象的 UIContextMenu 属性设置为所需文本菜单的标签名。在应用程序 M 文件中给每个文本菜单项 添加一个回调子函数,当用户选择特定的文本菜单项时,这个回调子函数将被调用。 12.4.5 GUI 组态 调用 GUIDE 命令来显示一个空的界面设计编辑器和一个无标题的图形窗口。在为该图 形窗口添加组件时,首先应该使用 GUIDE 应用程序选项对话框来进行 GUI 组态。选择界面 设计编辑器的 Tools 菜单下的“Application Options”选项打开选项对话框。GUIDE 应用程序 选项对话框的外观如图 12-28 所示。 –467– MATLAB 7.0 图 12-28 GUIDE 应用程序选项对话框 该对话框能够设置的选项包括以下几项: • 窗口重画行为(Resize behavior); • 命令行访问(Command-Line Accessibility); • 生成 FIG 文件和 M 文件(Generate FIG-File and M-File); • 生成回调函数原型(Generate Callback Function Prototypes); • 同一时刻仅允许运行一个应用程序实例(GUI Allows Only One Instance to Run (Singleton) ); • 使用系统背景颜色设置(Using the System Background Colors); • 仅生成 FIG 文件(Generate FIG-File Only)。 1.重画行为(Resize behavior) 通过选项对话框可以决定用户是否可以重画 GUI 所在的图形窗口以及 MATLAB 7.0 将如 何管理重画过程。GUIDE 提供以下 3 种选择: • Non-resizable:用户不能改变窗口大小(默认选项); • Proportional:允许 MATLAB 7.0 按照新的图形窗口尺寸自动按比例重新绘制 GUI 组件。 注意重画过程中将不改变组件标签字体的大小,如果图像窗口过大,那么组件的标签 将不可读。对于在设置过程中始终不关闭的简单 GUI 工具和对话框来说,这种方式非 常实用; • User-specified:通过编程使重画过程中 GUI 按照用户指定的方式变化。该选项需要用 户编写一个 ResizeFcn 属性定义的回调函数,该函数根据新的图形窗口尺寸重新计算 组件的大小和位置。 2.命令行访问(Command-Line Accessibility) 在 MATLAB 7.0 中创建一幅图形时,该图形的图形窗口和坐标轴将包括在其父对象的子 对象列表中,它们的句柄可以使用诸如 findobj、set 和 get 之类的函数来获得。创建 GUI 时也 要创建一个图形窗口,由于绘图命令会直接将结果输出到当前的 GUI 的图形窗口中,所以通 常情况下用户不需要额外的 GUI 图形窗口访问,就可以直接将图形输出到窗口中。但是,如 果用户需要创建一个包含坐标轴等绘图工具的 GUI,那么访问图形窗口则是必要的。这种情 –468– 第 12 章 高级图形设计 况下 GUI 要支持命令行的访问。 GUIDE 应用程序选项对话框提供 3 种用户访问权限。 • off:禁止命令行对 GUI 图形窗口的访问;在这种方式下 GUI 图形句柄是隐藏的,这 就意味着用户不能够使用 findobj 函数来定位 GUI 中的用户控件句柄。应用程序 M 文 件将创建一个对象句柄结构体来保存 GUI 中的所有用户控件句柄并将该结构体传递 给子函数保证 GUI 中句柄的使用无误; • on:允许命令行对 GUI 图形窗口进行访问; • User-specified:GUI 使用用户设置 Handle Visibility 和 IntergerHandle 这两个图形窗口 属性值,它们决定句柄是否能被命令行获得。Handle Visibility 属性决定图形窗口的句 柄对视图访问当前图形窗口的命令是否可见;该属性设置为 off 将导致图形窗口的句 柄从根对象的子对象列表中删除,使该图形窗口不再是当前图形窗口。但是该图形窗 口的句柄仍然有效,明确使用该句柄的函数仍然能够工作。IntergerHandle 决定图形窗 口的句柄是一个整数还是浮点数。如果该属性是 off,MATLAB 7.0 将使用一个不可重 复使用的浮点数来代替整数,这将大大减小用户对图形窗口的误操作几率。 3.生成 FIG 文件和 M 文件(Generate FIG-File and M-File) 如果用户希望 GUIDE 同时创建 FIG 文件和应用程序 M 文件,在 GUIDE 应用程序选项 对话框中选择“Generate FIG-File and M-File”选项。一旦用户选择了这个选项,用户就可以 选择以下任何一项 M 文件的组态项目。 • 生成回调函数原型(Generate Callback Function Prototypes):当用户在 GUIDE 用程序 选项对话框中选择该选项时,GUIDE 将在应用程序 M 文件中为每一个组件添加一个 回调函数(注意组合框和静态文本组件不使用自身的 Callback 属性)。用户必须为回 调函数编写代码。GUIDE 还为用户添加了一个与弹出式(右键鼠标出现)文本菜单选 项对应的回调子函数,用户无论何时选择菜单项都调用该函数; • 同一时刻仅允许运行一个应用程序实例(GUI Allows Only One Instance to Run (Singleton) ):该选项允许用户选择图形窗口的两种行为。允许 MATLAB 7.0 的一次 运行过程中仅有 GUI 一个实例;允许 MATLAB 7.0 显示 GUI 的多个实例。如果用户 允许运行一个实例,那么无论何时执行一个发布 GUI 的命令,MATLAB 7.0 都将重新 使用已存在的 GUI 图形窗口。如果 GUI 已存在,MATLAB 7.0 将该 GUI 带到前台而 不是重新创建一个新的窗口。如果允许 MATLAB 7.0 显示 GUI 的多个实例,则每一个 GUI 调用命令都将创建一个新的窗口。GUIDE 通过在应用程序 M 文件中生成使用 openfig 命令的代码来实现这个特征,而通过 reuse 或 new 字符来指定 GUI 的一个或多 个实例; • 使用系统背景颜色设置(Using the System Background Colors):GUI 组件使用的颜色 与计算机系统有关。选项使图形窗口的背景颜色与用户控件默认背景颜色(与系统有 关)相匹配。图 12-29 中左边的图形窗口未选择该选项,而右边的图形窗口选则了该 选项,可以看到二者的效果差异。 –469– MATLAB 7.0 图 12-29 背景颜色使用系统颜色设置与否的比较 如果用户不希望 GUIDE 生成应用程序 M 文件,在 GUIDE 应用程序选项对话框中选择 Generate FIG-File Only 选项。当用户在界面设计编辑器中保存 GUI 时,GUIDE 将仅仅创建能 够使用 open 和 hgload 命令重新显示的 FIG 文件。当用户选择这个选项时,必须将每一个 GUI 组件的 Callback 属性设置为一个 MATLAB 7.0 能够理解并执行指定操作的字符串,这个字符 串可以是一个表达式也可以是一个 M 文件。如果用户希望生成一个与应用程序 M 文件完全 不同的程序范例时可以选择这个选项。 12.4.6 GUI 界面设计 用户可以使用 openfig、open 或 hgload 命令来显示一个 GUI 图形窗口,这些命令将相应 的 FIG 文件装载到 MATLAB 7.0 的工作平台中,然后用户就可以对该 GUI 进行重新设计。用 户可以使用界面设计编辑器 File 菜单中的“Open”选项或以下命令来装载一个已存在的 GUI 进行编辑: guide mygui.fig GUI 界面设计是通过使用界面设计编辑器进行的。基本的编辑方法在前面已经做了详细 介绍,这里就不再赘述。 GUI 的创建过程中经常会要求用户定义组件的 Tag 属性值和 callback 子函数名。GUIDE 要求用户为 GUI 界面中的每个组件指定一个 Tag 属性值,然后根据该字符串来命名回调函数。 在第一次激活或保存 GUI 之前最好为组件选择一个 Tag 名和文件名。 使用界面设计编辑器 File 菜单的 Save as 选项可以给应用程序 M 文件重命名并重新设置 callback 属性以保证回调函数的正常运行。注意由于 GUIDE 使用 Tag 属性来命令函数和结构 体的域名,所以用户选择的 Tag 必须为一个有效的 MATLAB 7.0 变量名。可以使用 isvarname 函数来确定用户指定的字符串是否有效。如果用户在 GUIDE 中创建 M 文件和 FIG 文件后, 对 GUI 进行了修改,那么必须确保用户的代码能够兼容这些修改。 GUIDE 自动给每一个用户控件的 Tag 属性分配一个字符串并使用该字符串构造生成的回 调子函数名,并为句柄结构体添加一个域名。 如果用户在 GUIDE 生成回调子函数后对 Tag 属性进行了修改,GUIDE 将不会生成一个 新的子函数。然而,由于句柄结构体是实时创建的,所以 GUIDE 将在句柄结构体中使用新 的 Tag 名。避免产生问题最好的方法就是在添加组件的同时设置 Tag 属性值。如果一定要在 –470– 第 12 章 高级图形设计 应用程序 M 文件创建后修改 Tag 并希望重新命名回调子函数来保持 GUIDE 所使用的名称一 致,那么用户需要修改句柄结构体所有输出数据参考和修改回调子函数名称。当用户保存或 激活 GUI 时,GUIDE 将使用一个在应用程序 M 文件中执行回调子函数的字符串来替换每一 个取值为 automatic 的 Callback 属性。当第一次将一个按钮插入界面中时,其 Callback 属性 如图 12-30 所示。 图 12-30 按钮组件的 Callback 属性 图 12-31 添加第一个按钮的字符串 用户保存或激活图形窗口时,GUIDE 将修改 Callback 属性为执行回调子函数字符串,图 12-31 表明了添加了第一个按钮的字符串。 如果用户希望修改回调子函数的名称,那么必须同时修改用户控件的 Callback 属性字符 串。图 12-32 为回调子函数重命名为 Closebutton_Callback 后的外观。 图 12-32 回调子函数重命名后的外观 GUI 的 FIG 文件和相关的应用程序 M 文件的文件名相同,只是文件扩展名不同。当用 –471– MATLAB 7.0 户执行 M 文件以发布 GUI 时,使用 mfilename 命令从 M 文件名来确定 FIG 文件名: fig = openfig(mfilename,'reuse'); 如果 FIG 文件名与 M 文件名不同,以上语句将不会被成功调用。 对于 GUI 实现的第 3 个步骤——回调函数编程将在下一节向读者介绍。 完成 GUI 的创建工作后,使用 File 菜单下的“Save”或“Save as”选项将用户界面保存 为一个 FIG 文件。当用户保存或激活图形窗口时,GUIDE 将自动创建应用程序 M 文件。执 行 M 文件来显示 GUI。对于那些用户尚未实现,而 GUIDE 在 M 文件中保存原型的回调函数, 运行 MATLAB 7.0 时将返回一个消息说明这些函数尚未实现。 12.5 编程设计 GUI 在前面的基础上,本节将主要介绍 GUI 的深入编程方法。首先将说明应用程序 M 文件 系统生成代码的具体含义以及利用句柄结构体管理 GUI 数据的方法,然后介绍 GUI 组件回 调函数的类型和回调函数的中断方法,最后说明如何控制 GUI 图形窗口的行为。 12.5.1 M 文件以及 GUI 数据管理 1.应用程序 M 文件理解 GUI 包含许多可以使软件与用户中断进行交互的用户界面组件,GUI 的实现任务之一就 是控制这些组件如何响应用户的行为。对应用程序 M 文件代码进行详细分析的目的就是要通 过了解 GUIDE 创建应用程序 M 文件的功能,从而实现 GUI 的规划。 MATLAB 7.0 通过创建应用程序 M 文件为 GUI 控制程序提供一个框架。这个框架孕育 着一种高效而坚固的编程方法,即所有代码(包括回调函数)都包含在应用程序 M 文件中, 这就使得 M 文件仅有一个入口可以初始化 GUI 或调用相应的回调函数以及 GUI 中希望使用 的任意帮助子程序。无论用户是否使用 GUIDE 来创建应用程序 M 文件,这里所说的编程技 术对用户进行 GUI 编程都是有用的。 (1)回调函数自动命名 GUIDE 给添加到应用程序 M 文件中的回调子函数自动命名。GUIDE 还将 Callback 属性 值设置为一个字符串使用户激活控件时该子函数能够被调用。 首先说明 GUIDE 如何为回调子函数命名。当用户在 GUI 界面中添加一个组件时,GUIDE 为该组件的 Tag 属性指定一个用来生成回调函数名称的值。例如,假设用户添加到界面中的 第一个按钮被称为 pushbutton1,当用户保存或激活图形窗口时,GUIDE 在应用程序 M 文件 中 添 加 一 个 名 为 pushbutton1_Callback 的 回 调 子 函 数 。 如 果 用 户 为 该 按 钮 定 义 了 ButtonDownFcn 属性,则相应的回调子函数的名称为 pushbutton1_ButtonDownFcn。 GUIDE 通过指定回调字符串为回调子函数命名。当用户第一次为 GUI 界面添加一个组 件时,其 Callback 属性被设置为字符串,当用户保存或激活 GUI 时,该字符串将 通知 GUIDE 使应用程序 M 文件中相应的回调子函数名来替换该字符串。 –472– 第 12 章 高级图形设计 (2)应用程序 M 文件的执行路径 应用程序 M 文件根据 GUI 调用文件时所传递的参数类型来决定有待执行的行为。例如, 如果不向 M 文件传递任何参数,则调用 M 文件将会发布该 GUI(如果此时用户指定了一个 M 文件的输出参数,那么 M 文件将返回 GUI 图形窗口的句柄);如果使用一个子函数名作为 传递给 M 文件的第一个参数,那么调用 M 文件将会执行指定的子函数(通常是回调子函数)。 应用程序 M 文件包含一个调度函数,该函数可以使 GUI 能够根据调用方式决定执行路 径。应用程序 M 文件调度函数的功能是通过在 if 语句中使用 feval 函数实现的。在调用 M 文 件时,feval 函数将执行字符串参数所指定的子函数。feval 函数在一个 try/catch 调试语句块中 执行,这是因为当 GUI 视图调用不存在的子函数(找不到与传递参数名称相同的子函数)或 调用发生错误时能够得到正确的处理。以下是 GUIDE 生成的调度函数功能代码(用户不能 够修改): %若无输入参数,则打开 GUI if nargin ==0 fig = openfig(mfilename,'reuse'); … %如果输入参数为字符串,执行相应子程序 elseif ischar(varargin{1}) try [varargout{1:nargout}] = feval(varargin{:}); catch disp(lasterr); end end 任何由子函数返回的输出参数都将通过该函数返回 GUI。虽然 GUIDE 生成的回调子函 数的参数是明确的,但是参数列表的长度是变化的,这是由于输入参数 varargin 其实可以是 多个参数,用户可以在调用 M 文件时通过该参数给被调用的子函数赋予任意多个用户所需的 参数。用户可以通过编辑 Callback 属性字符串来传递额外的参数。 (3)GUI 初始化 首先,应用程序 M 文件使用 openfig 命令来装载 GUI 图形窗口,格式如下: fig = openfig(mfilename,'reuse'); 一定要注意这个语句打开的 FIG 文件名是源于应用程序 M 文件的(mfilename 命令返回 当前执行的 M 文件名),如果用户使用由 GUIDE 创建的应用程序 M 文件,那么用户必须保 证 FIG 文件与 M 文件同名。参数 reuse 决定任何时候都只能由一个 GUI 实例在运行。 应用程序 M 文件自动包含一些管理 GUI 的有效技术。 • 单个/多个实例控制。当设计 GUI 时,用户必须明确选择是否允许 GUI 图形窗口的多 个实例窗口存在。如果选择允许,也就是说同一时刻只能有一个实例存在,那么以后 视图创建另一个 GUI 的操作将仅仅导致已存在的 GUI 出现在其他窗口之上。很多信 息对话框(尤其是模态对话框)只能同时存在一个实例,因为对于用户的某种特定行 为,对话框只能提示一次。GUIDE 界面设计编辑器实际上是一个允许多个实例存在的 –473– MATLAB 7.0 GUI,设计这个 GUI 的目的是使用户能够同时打开多个界面; • GUI 图形窗口在屏幕中的位置不受目标计算机屏幕和分辩率的影响。应用程序 M 文件 使用 movegui 命令来确保 GUI 图形在目标计算机的屏幕上可见,使 GUI 不受计算机 屏幕的尺寸和分辨率的影响。如果指定的窗口位置将导致 GUI 窗口位于屏幕之外,那 么 movegui 命令将窗口移动到屏幕中离指定位置最近的地方。movegui 语句格式如下: movegui(fig,'onscreen')。其中 fig 是由 openfig 命令返回的 GUI 图形窗口句柄; • 自动创建 GUI 组件句柄结构体; • 自动命名 Tag 属性、生成子函数原型并指定回调属性字符串。当用户发布 GUI 时,应 用程序 M 文件创建一个包含所有 GUI 组件句柄的结构体,然后将该结构体保存在图 形窗口的应用程序数据中以备将来的使用。句柄结构体的域名与相应对象的 Tag 属性 值一致。 用户可以使用相同的方法访问隐藏的图形窗口句柄,例如假设窗口的 Tag 属性为 figure1, 则 handles.figure1 就是图形窗口的句柄。 应用程序 M 文件使用 guihandles 和 guidata 来创建并存储句柄结构体: handles = guihandles(fig); guidata(fig,handles); 注意只有那些 Tag 属性值为有效字符串的对象的句柄才能够保存在句柄结构中。可以使 用 isvarname 来确定字符串是否有效。 句柄结构体是传递给所有回调函数的参数之一,因而用户可以使用这个结构体来保存数 据并在子函数之间传递。 • 单个 M 文件同时包含 GUI 初始化和回调函数执行代码。 2.GUI 数据管理 GUIDE 使用保存在 GUI 图形窗口中的应用程序来定义和实现数据的存储和获取机制。 GUIDE 使用这个机制来存储一个包含所有 GUI 组件对象句柄的结构体。由于这个结构体将 传递每一个对象的回调函数,所以该结构体也可以用来保存其他数据。 12.5.2 回调函数的使用方法 1.回调函数类型 实现一个 GUI 的首要机制就是构成用户界面的用户控件的回调函数进行编程。除了用户 控件的 Callback 属性,还可以使用其他一些属性来定义回调函数。 (1)所有图形对象的回调函数属性 所有图形对象都有 3 个能够定义回调函数的属性: • ButtonDownFcn:当用户将鼠标放置在某个对象或对象相邻的 5 个像素范围内时,如 果单击鼠标左键,MATLAB 7.0 将会执行回调函数; • CreatFcn:MATLAB 7.0 将在创建对象时调用回调函数; • DeleteFcn:MATLAB 7.0 在删除对象之前调用回调函数。 –474– 第 12 章 高级图形设计 (2)图形窗口的回调属性 图形窗口有如下所述的几种额外的用来执行相应用户行为的属性。 • CloseRequestFcn:当请求关闭图形窗口时 MATLAB 7.0 将执行这个回调函数; • KeyPressFcn:当用户在图形窗口内按下鼠标键时 MATLAB 7.0 将执行这个回调函数; • ResizeFcn:当用户重画图形窗口时 MATLAB 7.0 将执行这个回调函数; • WindowButtonDownFcn:一旦用户在图形窗口内无控件的地方按下鼠标键时, MATLAB 7.0 就会执行这个回调函数; • WindowButtonMotionFcn:当用户在图形窗口中移动鼠标时,MATLAB 7.0 将执行这个 回调函数; • WindowButtonUpFcn:当用户在图形窗口中释放鼠标键时,MATLAB 7.0 将执行这个 回调函数。 MATLAB 7.0 将根据用户的行为来判断究竟执行哪个回调函数。单击一个有效的用户控 件将会阻碍任何 ButtonDownFcn 和 WindowButtonDownFcn 回调函数的执行,而如果用户单 击一个无效用户控件、图形窗口或其他定义了回调函数的图形对象时,MATLAB 7.0 将首先 执行图形窗口的 WindowButtonDownFcn 函数,然后再执行鼠标单击对象的 ButtonDownFcn 函数。 2.回调函数执行中断 默认情况下 MATLAB 7.0 允许正在执行的回调函数被后来调用的回调函数中断。例如, 假设用户创建了一个在装载数据时能够显示一个进展条的对话框,这个对话框包含一个取消 按钮可以组织数据的装载操作,那么取消按钮的回调函数将会中断正在执行的数据装载子函 数。某些情况下用户可能不希望正在执行的回调函数被用户的行为中断,例如,在重新显示 一幅图形之前,可能会需要使用一个数据分析工具进行数据流长度计算。假设用户行为可以 中断回调函数的执行,此时如果用户无意中单击鼠标使回调函数执行中断,那么就可能导致 MATLAB 7.0 在返回原来的回调函数之前发生状态改变,引起执行错误。 (1)可中断设置 所有图形对象都有一个控制其回调函数能否被中断的属性 Interruptible,该属性的默认值 为 on,表示回调函数可以中断。然而 MATLAB 7.0 只有在遇到一些特定的命令(drawnow、 figure、getfreame、pause 和 waitfor)时才会执行中断,转而查询事件序列,否则将会继续执 行正在执行的回调函数。在回调函数中出现的计算或指定属性值的 MATLAB 7.0 命令将会被 立即执行,而影响图形窗口状态的命令将被放置在事件序列中。事件可以由被任何导致图形 窗口重画的命令或用户行为引发,例如定义了回调函数的鼠标移动行为。仅仅当回调函数执 行完毕或回调函数包含 drawnow、figure、getframe、pause 和 waitfor 命令时,MATLAB 7.0 才进行事件序列的处理。 如果在回调函数的执行过程中遇到上述某个命令,MATLAB 7.0 将先执行的程序挂起, 然后处理事件序列中的事件。MATLAB 7.0 控制事件的方式依赖于事件类型和回调函数对 Interruptible 属性的设置。只有在当前回调对象的 Interrupotible 属性值为 on 的情况下,导致 其他回调函数执行的事件才可以真正执行回调函数;导致图形窗口重画的事件将无视回调函 数的 Interruptible 属性值而无条件地执行重画任务;对象的 DeleteFcn 属性和 CreatFcn 属性或 –475– MATLAB 7.0 图形窗口的 CloseRequestFcn 属性或 ResizeFcn 属性定义的回调函数将无视对象的 Interruptible 属性而中断正在执行的回调函数。 所有对象都具有一个 BusyAction 属性,该属性决定了对于在不允许中断的回调函数执行 期间发生的事件的处理方式。BusyAction 有两种可能的取值。 • queue:将事件保存在事件序列中并等待不可中断回调函数执行完毕后处理; • cancel:放弃该事件并将事件从序列中删除。 (2)回调函数执行期间的事件处理 以下几种情况描述了 MATLAB 7.0 在一个回调函数的执行期间是如何处理事件的。 • 如果遇到了 drawnow、figure、getframe、pause、waitfor 命令中的一个命令,那么 MATLAB 7.0 将该回调函数挂起并开始处理事件序列; • 如果事件序列的顶端事件要求重画图形窗口,MATLAB 7.0 将执行重画并继续处理事 件序列中的下一个事件; • 如果事件序列的顶端事件将会导致一个回调函数的执行,MATLAB 7.0 将判断回调函 数被挂起的对象是否可中断。如果回调函数可中断,MATLAB 7.0 执行与中断事件相 关的回调函数;如果该回调函数包含 drawnow、figure、getframe、pause、waitfor 命令 之一,那么 MATLAB 7.0 将重复以上步骤;如果回调函数不可中断,MATLAB 7.0 将 检查事件生成对象的 BusyAction 属性;如果该属性值为 queue,MATLAB 7.0 将事件 保留在事件序列中;如果 cancel 则放弃该事件; • 当所有事件都被处理后,MATLAB 7.0 恢复被中断函数的执行。 这些步骤都一直持续到回调函数执行完毕为止。当 MATLAB 7.0 返回命令窗口时,所 有残余的事件都已经被处理了。当然,由于序列中事件的类型不同,以上步骤有可能不一 一执行。 12.5.3 图形窗口的行为控制 在设计 GUI 时,需要考虑显示 GUI 时图形窗口将怎样展开,以及一个 GUI 图形窗口的 行为是用户所需要的。考虑以下几种情况: • 一个实现图形注释的工具 GUI 通常设计成其他 MATLAB 7.0 执行任务可访问的 GUI, 这个工具可能一次只能对一幅图形进行注释,所以每一幅图形都需要一个新的工具实 例; • 一个对话框,能向用户发出询问并阻止 MATLAB 7.0 运行直至用户作出回答。但是 用户可能需要通过观察其他的 MATLAB 7.0 窗口获得信息后才能够对该对话框作出 回答。 • 一个警告用户其指定的操作将会破坏文件的对话框,该对话框能够执行用户所需的 操作前强迫用户作出回答。此时的图形窗口既被阻止又是模态的(用户不可观察其 他窗口)。 以下 3 种技术能够有效地实现以上 GUI 的设计要求: • 允许单个或多个 GUI 实例同时运行; • 在显示 GUI 时阻止 MATLAB 7.0 的运行; –476– 第 12 章 高级图形设计 • 使用模态图形窗口使用户只能与当前执行的 GUI 进行交互。 模态图形窗口能够捕捉在 MATLAB 7.0 窗口任何可见位置处发生的键盘和鼠标事件。这 就意味着一个模态图形窗口能够处理用户与任何组件的交互操作,但是不允许用户访问其他 的 MATLAB 7.0 窗口(包括命令窗口)。另外,除非删除该模态窗口,否则窗口将始终位于 窗口堆栈的最上方。如果用户希望在进行 MATLAB 7.0 其他操作之前必须先响应 GUI,那么 最好使用模态图形窗口。 下面说明如何使一个 GUI 窗口模态化。事实上设置图形窗口的 WindowStyle 属性为 modal 就可将图形窗口模态化。用户可以使用属性检查器,也可以通过在应用程序 M 文件的初始化 阶段调用以下语句进行属性设置: set(fig,'WindowStyle','modal') 在这个语句中,set 函数使用由 openfig 函数返回的句柄实现设置工作。 如果要释放模态窗口的控制权,可以在模态窗口 GUI 的回调函数中使用以下方法之一: • 删除窗口:delete(figure_handle) • 使窗口不可见:set(figure_handle,'Visible','off'); • 修改窗口的 WindowStyle 属性值为 normal:set(figure_handle,'WindowStyle','normal')。 用户还可以在一个模态窗口按 ctrl+C 键将该窗口转换为普通窗口。 无论使用哪一种方法释放模态窗口控制权都需要获得该窗口的句柄。由于大多数的 GUI 将图形窗口句柄隐藏以避免无意识的访问,所以在回调函数中使用 gcbf 命令能够最有效地获 取窗口句柄。gcbf 返回当前回调对象所在窗口的句柄,这个句柄就是释放控制权所需的句柄。 例如,假设用户对话框包括一个用来关闭对话框的按钮,其回调函数末尾处将调用 delete 来 删除对话框,示例代码如下: function varargout = pushbutton1_Callback(h,eventdata,handles,varargin) %执行程序代码 … %用户响应对话框后删除窗口 delete(gcbf) 12.6 图形用户界面设计实例 实现一个用来显示名字和电话号码的 GUI,该 GUI 中输入的名字和电话号码都保存在一 个 MAT 文件中。要求该 GUI 可以添加新条目并将该条目保存在同一个 MAT 文件或一个新的 MAT 文件中。 12.6.1 图形界面的实现 要实现图形界面这个问题要用到以下几种 GUI 编程技术: • 使用打开和保存对话框的方法为使用者提供寻址和打开地址簿 MAT 文件、保存 MAT 文件修改、创建新地址簿的方法; • 定义 GUI 菜单回调函数; –477– MATLAB 7.0 • 使用 GUI 句柄结构体保存和传递全局数据(名字和电话号码); • 使用 GUI 图形窗口重画函数实现重新显示工作。 这里仅仅介绍 GUI 组态和界面设计工作,关于回调函数的编程方法将在下一节介绍。 下面就介绍其具体解决方法。 步骤一:GUI 组态。打开 GUI 应用程序选项对话框,进行如下设置: • 重画行为:User-spcified; • 命令行可访问性:Off; • 同时生成 FIG 文件和 M 文件; 生成回调函数原型:Generate callback fuction prototypes; 同时只允许一个实例运行:Application allows only one instance to run。 步骤二:进行界面设计。在命令行中键入 guide,选择空白模板。 可以按照用户的喜好任意设计 GUI 界面并排列各组件,例如如图 12-33 所示的 GUI 界面。 使用“Prev”和“Next”按钮在地址簿中的条目向前或向后翻页。 图 12-33 地址簿的 GUI 界面 步骤三:设计菜单。地址簿 GUI 包含一个 File 菜单,其菜单项为 Open,GUI 用该菜单 项来装载地址簿 MAT 文件。当用户对地址簿进行修改时,用户需要保存被修改的 MAT 文件 或将这些修改保存为一个新的 MAT 文件,使用 File 菜单的“Save”和“Save as”菜单项使 用户能够实现这些要求。 步骤四:对各个组件的属性进行设置。在界面设计编辑器中使用菜单命令打开属性检查 器,根据用户的界面需要设置组件的 Tag、callback 等属性。必须设置“Prev”和“Next”按 钮的 Callback 属性以及 Save 和 Save as 菜单项的回调函数,使它们分别能够调用同一个回调 函数 Save_Callback、Prev_Next_Callback。另外,将窗口的 ResizeFcn 属性设置为下面的格式: address_book('ResizeFcn',gcbo,[],guidata(gcbo)) 步骤五:保存 GUI。激活 GUI 界面,确保界面符合用户要求。设计满意后保存 GUI。 步骤六:执行 GUI。运行地址簿 GUI 的应用程序 M 文件。可以对该 M 文件进行反复调 试使之符合用户的要求。 12.6.2 行为控制的实现 前一节已经介绍了地址簿界面的实现,下面来讲如何实现地址簿 GUI 的行为控制。在 GUI 中最重要的技术之一就是保证信息跟踪正确并使不同的子函数能够获得该信息。这里所 指的信息包括当前 MAT 文件名,MAT 文件名中的名字和电话号码;一个表明当前名字和电 –478– 第 12 章 高级图形设计 话号码,一个表明当前姓名和电话号码的索引指针(用户在地址簿中翻页时该指针必须刷新), 图形窗口位置和大小,所有 GUI 组件的句柄。为了能够正确地管理全局数据,地址簿 GUI 的行为控制主要实现以下几个方面: • 发布 GUI:由于 GUI 需要在其他 MATLAB 7.0 任务运行的同时执行,所以 GUI 需要 设置成不可中断、非模态的; • 将一个地址簿装入阅读器中:有两种方式可以将地址簿 MAT 文件装入 GUI 中:发布 GUI 时使用参数指定 MAT 文件,如果用户不使用参数,则 MATLAB 7.0 装入默认的 地址簿文件 addrbook.mat;用户使用 File 菜单的 Open 选项来装载其他 MAT 文件; • 管理菜单回调行为:选择 Open 菜单项后将显示一个对话框(uigetfile),使用户能够浏 览文件。对话框将返回文件名及其路径,并将返回值传递给 fullfile,从而确保路径对 于任意的操作系统而言是合理构建的;Save 和 Save as 菜单用来保存对 MAT 文件的修 改;Creat New 菜单将简单地清除 Contact Name 和 Contact Phone#编辑框的内容以便添 加新的名字和电话号码。在添加新条目后,用户使用 Save 或 Save as 菜单项来保存地 址簿。 • 管理按钮组件回调行为:Contact Name 编辑框显示地址簿条目的姓名,如果用户键入 新的姓名,若当前地址簿中已存在该姓名,相应的电话将被显示;如果该姓名不存在, 则显示一个询问对话框(questdlg)确认是否创建一个新的条目,如果不创建则返回到 先前显示的姓名;Contact Phone#编辑框显示与 Contact Name 编辑框条目相匹配的电 话号码。如果键入一个新的数字并回车,回调函数将发布一个询问对话框确认是否要 对已存在的号码进行修改。按钮控件 Prev 和 Next 用来实现地址簿条目间的翻页。 • 地址簿将自定义重画函数,允许用户增大窗口的宽度以适应长姓名和电话号码,但是 不允许缩小窗口宽度或改变窗口高度(这一限制不会限制 GUI 的使用,并且简化了必 须保持窗口尺寸与组件大小相匹配的重画函数代码)。用户重画窗口并释放鼠标后将执 行重画函数,重画后的窗口尺寸将被保存下来。 下面具体介绍解决方法: 步骤一:发布 GUI。用户可以不使用参数来调用应用程序 M 文件,在这种情况下 GUI 使用默认的地址簿 MAT 文件。用户也可以在参数中指定一个 MAT 文件,这就需要修改默认 的应用程序 M 文件 GUI 初始化代码。修改后的初始化代码如下: function varargout = address_book(varargin) if nargin <= 1 %GUI 发布 fig = openfig(mfilename,'reuse'); set(fig,'Color',get(0,'defaultUicontrolBackgroundColor')); handles = guihandles(fig); guidata(fig,handles); if nargin ==0 %装入默认的地址簿 Check_And_Load([],handles) elseif exist(varagin{1},'file') Check_And_Load(varargin{1},handles) –479– MATLAB 7.0 else %如果文件不存在,则返回一个错误对话框并将文本设置为空字符串 errordlg('File Not Found','File Load Error') set(handles.Contact_Name,'String','') set (handles.Contact_Phone,'String','') end if nargout >0 varargout{1} = fig; end elseif ischar(varargin{1}) %调用指定的子函数或回调函数 try [varargout{1:nargout}] = feval(varargin{:}); catch disp(lasterr); end end 步骤二:将一个地址簿装入阅读器中。首先要确认 MAT 文件。作为一个有效的地址簿 文件,MAT 文件必须包含一个称作 Addresses 的结构体,该结构体有两个域,分别称为 Name 和 Phone。Check_And_Load 子函数将按照以下步骤来确认并装载这些数据。装载指定的或默 认的文件;判断 MAT 文件是否为一个有效的地址簿文件,如果有效则显示数据,否则显示 错误对话框;对于有效的 MAT 文件,返回 1,否则返回 0(该返回值由 Open 菜单项回调函 数使用);在句柄结构中保存条目 MAT 文件名和 Addresses 结构体;指示当前所显示的名字 和地址的索引指针。 Check_And_Load 设置的代码如下: function pass = Check_And_Load(file,handles) % 初始化变量 pass 以判断文件是否有效 pass = 0; %如果不指定文件名则使用默认名称,否则如果指定文件存在就装载它 if isempty(file) file = 'addrbook.mat'; handles.LastFile = file; guidata(handles.Address_Book,handles) end if exist(file) == 2 data = load(file); end %判断 MAT 文件是否有效,当存在一个名位 Addresses 的变量 %并且其两个域名为 Name 和 Phone 时,该文件有效 –480– 第 12 章 高级图形设计 flds = fieldnames(data); if (length(flds) == 1) & (strcmp(flds{1},'Addresses')) fields = fieldnames(data.Addresses); if (length(fields) == 2) &(strcmp(fields{1},'Name')) & (strcmp(fields{2},'Phone')) pass = 1; end end %如果文件有效则显示 if pass %给句柄结构体添加地址 handles.Addresses = data.Addresses; %显示第一个条目 set(handles.Contact_Name,'String',data.Addresses(1).Name) set(handles.Contact_Phone,'String',data.Addresses(1).Phone) %将索引指针设置为 1 并保存句柄 handles.Index = 1; guidata(handles.Address_Book,handles) else errordlg('Not a valid Address Book','Address Book Error') end 步骤三:控制菜单回调行为。 首先看 Open 菜单项回调函数 Check_And_Load,该函数将确认并装载新的地址簿。 Open 菜单项的回调函数代码如下: function varargout = Open_Callback(h, eventdata, handles, varargin) [filename, pathname] = uigetfile( ... {'*.mat', 'All MAT-Files (*.mat)'; ... '*.*','All Files (*.*)'}, ... 'Select Address Book'); %选择 Cancel 则返回 if isequal([filename,pathname],[0,0]) return %否则构造文件全路径并检查装载这些文件 else File = fullfile(pathname,filename); %如果 MAT 文件无效,不保存信息 if Check_And_Load(File,handles) handles.LastFIle = File; guidata(h,handles) end end –481– MATLAB 7.0 再来看 Save 和 Save as 回调函数,该回调函数使用菜单项的 Tag 属性来判断究竟时 Save 还是 Save as 为回调对象,也就是说哪一个对象的句柄作为回调函数的第一个参数。如果用 户选择的是 Save 菜单项,则调用 save 命令来保存 MAT 文件的新名字和电话号码;如果选择 的是 Save as,则显示一个指定保存目标文件名的对话框,用户可以选择一个已有的文件,也 可以输入新的文件名,对话框将返回文件名和路径。 • 使用 fullfile 来创建一个与平台武官的路径名; • 调用 save 来保存 MAT 文件中的新数据; • 刷新句柄结构体以包含新的 MAT 文件名; • 调用 guidata 保存句柄结构体。 Save_Callback 回调函数代码如下: function varargout = Save_Callback(h, eventdata, handles, varargin) %获取被选菜单的 Tag 属性 Tag = get(h,'Tag'); %获取 address 数组 Addresses = handles.Addresses; %根据所选项执行相应的行为 switch Tag case 'Save' %保存到默认的地址簿文件中 File = handles.LastFile; save(File,'Addresses') case 'Save_As' %允许用户选择要保存的目标文件名 [filename, pathname] = uiputfile( ... {'*.mat';'*.*'}, ... 'Save as'); %如果选择了 Cancel 则返回 if isequal([filename,pathname],[0,0]) return else %构造全路径名并保存 File = fullfile(pathname,filename); save(File,'Addresses') handles.LastFile = File; guidata(h,handles) end end 第三步设置 Creat New 菜单。Creat New 菜单的回调函数主要将编辑框的 String 属性设置 为空。其代码如下: function varargout = New_Callback(h, eventdata, handles, varargin) –482– 第 12 章 高级图形设计 set(handles.Contact_Name,'String','') set(handles.Contact_Phone,'String','') 步骤四:控制组件回调行为。 首先看 Contact Name 编辑框回调函数。回调函数使用句柄结构体来访问地址簿的内容并 获得索引指针,这样回调函数就能够判断用户修改之前编辑框中显示的是什么姓名。索引指 针表明当前显示的姓名位置,在发布 GUI 时由 Check_And_Load 函数来添加地址簿和索引指 针。如果用户添加了一个新的条目,回调函数将新的姓名添加到地址簿中并刷新索引指针来 显示新的数值。刷新后的地址簿和索引指针再一次被保存在句柄结构体中。Contact Name 编 辑框的回调函数代码如下: function varargout = Contact_Name_Callback(h, eventdata, handles, varargin) %在 Contact Name 和 Phone 编辑框众获取字符串 Current_Name = get(handles.Contact_Name,'string'); Current_Phone = get(handles.Contact_Phone,'string'); %如果为空则返回 if isempty(Current_Name) return end %从句柄结构体中获取当前地址列表 Addresses = handles.Addresses; %搜索姓名列表,判断是否与一个已有姓名相同 for i = 1:length(Addresses) if strcmp(Addresses(i).Name,Current_Name) set(handles.Contact_Name,'string',Addresses(i).Name) set(handles.Contact_Phone,'string',Addresses(i).Phone) handles.Index = i; guidata(h,handles) return end end %如果是个新的姓名,请求创建一个新的条目 Answer=questdlg('Do you want to create a new entry?', ... 'Create New Entry', ... 'Yes','Cancel','Yes'); switch Answer case 'Yes' Addresses(end+1).Name = Current_Name; % Grow array by 1 Addresses(end).Phone = Current_Phone; index = length(Addresses); handles.Addresses = Addresses; handles.Index = index; –483– MATLAB 7.0 guidata(h,handles) return case 'Cancel' %恢复为初始数值 set(handles.Contact_Name,'string',Addresses(handles.Index).Name) set(handles.Contact_Phone,'String',Addresses(handles.Index).Phone) return end 再来看 Contact Phone #回调函数。和 Contact Name 编辑框类似,这个回调函数使用索引 指针来刷新地址簿中的新数值或恢复为上一次的显示内容。所有当前地址簿和索引指针都保 存在句柄结构体中,因而其他回调函数可以获得这些数据。以下是 Contact Phone #编辑框的 回调函数代码: function varargout = Contact_Phone_Callback(h, eventdata, handles, varargin) Current_Phone = get(handles.Contact_Phone,'string'); %如果有一个为空则返回 if isempty(Current_Phone) return end %在句柄结构体众获取当前地址表 Addresses = handles.Addresses; Answer=questdlg('Do you want to change the phone number?', ... 'Change Phone Number', ... 'Yes','Cancel','Yes'); switch Answer case 'Yes' % If no name match was found create a new contact Addresses(handles.Index).Phone = Current_Phone; handles.Addresses = Addresses; guidata(h,handles) return case 'Cancel' %恢复为初始值 set(handles.Contact_Phone,'String',Addresses(handles.Index).Phone) return en 最后看“Prev”和“Next” 按钮的回调函数。回调函数定义一个额外的参数 str 来表明 被单击的按钮是“Prev”还是“Next”。“Prev”按钮的回调字符串以“Prev”为最后一个参数, 而“Next”按钮则以“Next”为最后一个参数。在 case 语句中用 str 的数值来区分并实现每 一个按钮的功能。Prev_Next_Callback 回调函数将从句柄结构体中获取当前的指针和地址, 并且根据被选择的按钮来增加或减小索引指针的数值,并显示相应的名字和电话号码,最后 –484– 第 12 章 高级图形设计 将在句柄结构体中存储新的索引指针数值并使用 guidata 来保存刷新后的结构体。 Prev_Next_Callback 回调函数代码如下: function varargout = Prev_Next_Callback(h, eventdata, handles, str) %获取索引指针和地址 index = handles.Index; Addresses = handles.Addresses; %根据被单击的按钮修改显示结果 switch str case 'Prev' %索引值减 1 i = index - 1; %如果索引值小于 1,设置其为 Addresses 数组最后一个元素的索引 if i < 1 i = length(Addresses); end case 'Next' %索引值增加 1 i = index + 1; %如果索引值超出范围,设置其为 Addresses 数组第一个元素的索引 if i > length(Addresses) i = 1; end end %为被选索引获取相应数据 Current_Name = Addresses(i).Name; Current_Phone = Addresses(i).Phone; set(handles.Contact_Name,'string',Current_Name) set(handles.Contact_Phone,'string',Current_Phone) %刷新索引指针以反映新的索引 handles.Index = i; guidata(h,handles) 步骤五:自定义重画函数。重画函数将对以下几种可能发生的情况进行处理。 • 改变宽度:如果新的宽度大于固有宽度,图形窗口将被设置为新的宽度。Contact Name 编辑框的尺寸随着窗口宽度的变化而变化。改变编辑框的宽度需要修改编辑框的 Units 为 normalized,重新设置编辑框的宽度为窗口宽度的 78.9%,将 Units 重新设置为 characters。如果新的宽度小于固有宽度,窗口被设置为固有宽度; • 改变高度:如果用户视图修改窗口高度,高度始终被设置为固有高度。但是由于用户 释放鼠标时会调用重画函数,所以重画函数不能总判断 GUI 在屏幕中的固有位置。因 此,重画函数对窗口垂直分量作鼠标释放时的垂直位置+鼠标释放时的高度-原始高 –485– MATLAB 7.0 度的设置。 当窗口从底部被重画时,窗口将保持原位;从顶部重画时,窗口将移动到鼠标释放处的 位置。 重画函数调用 movegui 函数来确保无论用户在何处释放鼠标,重画后的窗口始终保持在 屏幕中。第一次发布 GUI 时,GUI 窗口的大小和位置由 Position 属性决定,用户可以使用属 性检查器来设置这个属性。 重画函数代码如下: function ResizeFcn(hObject, eventdata, handles) %获取窗口尺寸和位置 Figure_Size = get(hObject, 'Position'); %设置窗口固有尺寸 Original_Size = [ 0 0 94 19.230769230769234]; %如果重画窗口小于固有窗口尺寸,实行补偿 % original figure size then compensate if (Figure_Size(3)> notebook -setup 就会得到如下代码: Welcome to the utility for setting up the MATLAB Notebook for interfacing MATLAB to Microsoft Word Choose your version of Microsoft Word: [1] Microsoft Word 97 [2] Microsoft Word 2000 [3] Microsoft Word 2002 (XP) MATLAB 7.0 [4] Exit, making no changes (3)根据安装的 Word 的版本选择相应的代号,本文选择如下: Microsoft Wor d Version: 2 Notebook setup is complete. 这样你就可以使用 MATLAB 7.0 Notebook 了。通过下面地两种方法可以打开一个 M-book 文件。 (1)直接在 MATLAB 7.0 命令窗口键入命令来新建或打开一个 M-book 文件: >> notebook %新建一个 M-book >> notebook c:\documents\mymbook.doc %打开一个已经存在的 M-book (2)直接打开 M-book.dot 文件,如图 13-1 所示。 图 13-1 打开的 M-book.dot 文件 13.2 一个 Notebook 实例 使用 Notebook,在某种意义上就是在 Word 中使用 M-book 模板文档。Notebook 是通过 动态链接和 MATLAB 7.0 交互的。Notebook 和 MATLAB 7.0 交互的基本单位为细胞(在 Notebook 中,Word 和 MATLAB 7.0 之间交换的信息叫做“细胞”)。M-book 需要把在 Word 中输入的 MATLAB 7.0 命令或者语句行组成细胞,再传到 MATLAB 7.0 中运行,运行输出的 结果再以细胞的方式传回 Notebook。 1.在 Word 中执行命令的基本过程 Notebook 采用输入细胞(input cell)来定义 MATLAB 7.0 的输入命令。具体操作步骤如下: • 采用文本格式输入命令,在命令结束时不要按回车和空格键; • 从 Notebook 菜单中选中“Define Input Cell”选项,用来定义输入细胞; –488– 第 13 章 MATLAB 7.0 与 Word、Excel 的混合使用 • 从 Notebook 菜单中选中“Evaluate Cell”选项或者按 Ctrl+Enter 键。 其中输入细胞都显示为黑方括号包括的绿色字符,输出细胞都是黑方括号包括的蓝色字 符,如果出现错误黑方括号包括的红色字符,其他文本都默认为黑色字符。如下:(作者注释: 无专门标示的行为蓝色) m=eye(3) (作者注释:此行为绿色) m= 100 010 001 m/0 (作者注释:此行为绿色) Warning: Divide by zero. (Type "warning off MATLAB 7.0:divideByZero" to suppress this warning.) ans = Inf NaN NaN NaN Inf NaN NaN NaN Inf m=eye(3) n=eye(4) (作者注释:此行为绿色) ??? format compact;m=eye(3) n=eye(4) | (作者注释:此行以及上下两行为红色) Error: Missing operator, comma, or semicolon. (此行为绿色) 2.实例讲解 【例 13.1】在一段文本中间执行代码。 在 MATLAB 7.0 中,可以把输入细胞放在文本中间运行,而不影响其他文本。步骤如下: • 输入样本如下,将 m=eye(3)放到文本中间。 m=eye(3) • 选中命令,代码设置如下: m=eye(3) • 从 Notebook 菜单中选中“Evaluate Cell”选项或者按 Ctrl+Enter 键。将会出现上面例 子中类似的结果。 • 如果要将输出细胞转化为普通文本,选中要转换的细胞,然后从 Notebook 菜单中选中 “Undefine Cells”选项或者按 Alt+U 键。执行结果为: m=eye(3) m= 100 010 001 【例 13.2】绘制一幅图片。 生成完整图形的多条图形指令必须定义在同一细胞群中: t=0:0.1:20;y=1-cos(t).*exp(-t/5); –489– MATLAB 7.0 Time=[0,20,20,0]; Amplitude=[0.95,0.95,1.05,1.05]; fill(Time,Amplitude,’g’),axis([0,20,0,2]); xlabel(’Time’),ylabel(’Amplitude’); hold on plot(t,y,’r’,’LineWidth’,2) hold off ymax=min(y) 将以上程序写到 Word 当中,然后全部选上,然后选择“Notebook|Define Input Cell”,最 后从 Notebook 菜单中选中“Evaluate Cell”选项,或者按 Ctrl+Enter 键,得到的结果如图 13-2 所示。 1.6 1.4 1.2 1 0.8 0.6 0.4 0.2 0 0 2 4 6 8 10 12 14 16 18 20 图 13-2 图片显示结果 13.3 Notebook 使用的几个问题 最后强调一下 Notebook 使用中应该特别注意的几个问题。 • M-book 文档即在 Word 中输入的 MATLAB 7.0 函数命令、语句或程序,其指令与标点 符号都必须在英文状态下输入; • 带鼠标操作的图形 MATLAB 7.0 交互指令最好不在 M-book 文档中运行; • Windows 是一种多任务操作系统。但在运行 M-book 文档时,最好不运行其他程序, 不执行其他任务,以免影响 M-book 文档中程序的正确执行; • 由于计算机硬件与软件配合方面诸多不确定因素的影响,M-book 文档中的程序执行可 能出现异常情况。这可以用以下方法解决:第一,MATLAB 7.0 主程序首行用语句 clear 以清除内存中的变量和函数;第二,重新启动计算机后,再执行 M-book 文档中的程 序;第三,将 M-book 文档中的程序拷贝为 M 文件,然后到 MATLAB 7.0 的命令窗口 –490– 第 13 章 MATLAB 7.0 与 Word、Excel 的混合使用 下去执行; • MATLAB 7.0 指令在 M-book 文档中运行的速度比在 MATLAB 7.0 命令窗口中执行要 慢很多,但这对撰写科技文章或数据没有什么影响; • 可将细胞转换为普通文本。输入细胞在 Word 里编辑的文件中是绿色字符,而输出细 胞是蓝色字符,这会影响 Word 文件的印刷效果。可以将 Notebook 中的输入细胞、输 出细胞转换为普通文本,其转换步骤为先将光标放在细胞中,然后选中 Notebook 菜单 中的“Undefine Cells”选项命令或者按组合键 Alt+U。还可以用鼠标左键选中输入细 胞即“刷黑”,接着运行 Notebook 菜单中的“Undefine Cells”子项命令,全部输入细 胞和输出细胞都会被转换为文本格式显示,黑色的方括号对也被取消。所有绿色字符 的输入细胞与蓝色字符的输出细胞均变成黑色字符。此时黑色字符为“Courier New” 字体的 10 号字。在 Word 中,就可以按照撰写要求对其进行编辑; • 如果需要的话,使用 Notebook 菜单中的“Bring MATLAB 7.0 to Font”命令或者按组 合键 Alt+M 可以把 MATLAB 7.0 的命令窗口调到前台; • 使用 Notebook 菜单中的“Toogle Graph Output for Cell”命令可以控制是否显示输入细 胞或输出细胞的输出图形。若控制为不输出图形,可将光标置于欲运行的细胞内,选 中该“Toggle Graph Output for Cell”命令项,在细胞后将生成“no graph”,运行细胞 就不输出图形;再一次选中该项,即可输出图形。 13.4 Excel link 的安装和使用环境 Excel link 是一个在 Microsoft Windows 环境下实现对 Microsoft Excel 和 MATLAB 7.0 进 行链接的插件。通过对 Excel 和 MATLAB 7.0 的链接,用户可以在 Excel 的工作空间里,利 用 Excel 的宏编程工具,使用 MATLAB 7.0 的数据处理和图像处理功能进行相关操作,同时 由 Excel link 来保证两个工作环境中数据的交换和同步更新。使用 Excel link 时,不必脱离 Excel 环境,而是直接在 Excel 工作区或宏操作中调用 MATLAB 7.0 函数。Excel link 提供了 11 条功能函数来实现数据的链接和操作,对这些函数将在后面的小节中陆续说明。 13.4.1 Excel link 的安装 系统需要在 Windows 环境下先安装 Excel,然后再安装 MATLAB 7.0 和 Excel link。Excel link 的安装可以按 MATLAB 7.0 安装向导的提示进行,即在 MATLAB 7.0 安装组件选择框中 选中 Excel link,并按提示继续安装。 安装完毕 Excel link 后,还需要在 Excel link 中做相应的设置,完成 Excel 与 Excel link 的链接。具体操作如下: • 启动 Microsoft Excel,单击工具菜单(Tools),执行“加载宏”命令,如图 13-3 所示。 –491– MATLAB 7.0 图 13-3 “加载宏”菜单 • 在打开的“加载宏”对话框中单击“浏览”按钮,选择用户自己的\matlab\toolbox\exlink 路径下的 excllink.xla 文件,然后单击“确定”按钮,如图 13-4 所示。 图 13-4 加载的宏文件 • 返回“加载宏”窗口,此时已经选中了“Excel link”选项,如图 13-5 所示。单击“确 定”按钮后,Excel link 插件即可加载 MATLAB 7.0,并可以看到其运行窗口。 注意:这个 MATLAB 7.0 窗口是以传统方式打开的,也就是说没有启动 Java 界面。 –492– 图 13-5 已经选中 Excel link 界面 第 13 章 MATLAB 7.0 与 Word、Excel 的混合使用 13.4.2 设置 Excel link 的启动方式 当用户按照上一节的步骤安装了 Excel link,并在 Excel 中进行了确认之后,在每一次启 动 Excel 的时候,Excel link 和 MATLAB 7.0 将自动运行。如果不希望 Excel link 和 MATLAB 7.0 自动运行,那么可以通过在 Excel 数据表单元中输入“=MLAutoStart("no")”函数,即可 改变 Excel 的初始化文件中对自动启动 Excel link 和 MATLAB 7.0 的设置,如图 13-6 所示。 此时当再次启动该文件时,Excel link 和 MATLAB 7.0 就不会自动运行。 图 13-6 A1 单元中输入命令 用户也可以从 Excel 环境手动启动 Excel link 和 MATLAB 7.0。首先,在工具菜单中选择 “宏”菜单,如图 13-7 所示。 图 13-7 选择宏 在打开的“宏”对话框中输入“MATLABinit”,如图 13-8 所示,单击“执行”按钮后即 可启动 Excel link,并同时启动 MATLAB 7.0。 –493– MATLAB 7.0 图 13-8 输入“MATLAB 7.0init”指令启动 Excel link 以及 MATLAB 7.0 13.4.3 终止 Excel link 的运行 当终止 Excel 的时候,Excel link 和 MATLAB 7.0 会被同时终止。 如果需要在 Excel 环境中终止 MATLAB 7.0 和 Excel link 的运行,则在工作表单元中输入 “=MLClose()”。例如在 B1 单元中输入“=MLClose()”,如图 13-9 所示,确认后,将终止 MATLAB 7.0 和 Excel link 的运行,此时 B1 的状态如图 13-10 所示。 当需要重新启动 Excel link 和 MATLAB 7.0 时,可以选择用前面提到的 MLOpen 或 MATLABinit 命令来启动。 图 13-9 在 B1 单元中输入=mlclose() –494– 图 13-10 B1 的状态 13.5 一个 Excel link 实例 第 13 章 MATLAB 7.0 与 Word、Excel 的混合使用 本 节 的 应 用 举 例 中 涉 及 到 的 Excel 文 件 , 包 含 在 MATLAB 7.0 安 装 路 径 下 的 \toolbox\exlink 子目录下,文件名为:ExliSamp.xls。 在数据处理的过程中,使用回归和曲线拟合的目的是寻找最充分的反映变量之间的相互 关系的函数,从效果而言,它们试图建立基于某一数据集的数学模型,MATLAB 7.0 提供了 许多功能强大并且操作简便的矩阵操作和运算函数来支持此项业务。 本例中同时利用了回归分析和曲线拟合两种技术,并提供了工作表执行和宏命令执行两 种方式。在 Excel 数据表中管理和显示数据,由 Excel link 函数把数据表中的数据拷贝到 MATLAB 7.0 中并执行 MATLAB 7.0 的计算和图形函数。宏命令执行模式实现了把运算结果 返回给 Excel 数据表的功能。 13.5.1 数据表执行方式 启动 Excel、Excel link 和 MATLAB 7.0,打开示例文件 exlisamp.xls。 单击 exlisamp.xls 中的 Sheet1 标签,可以看到数据表中包含一个被命名为 DATA 的数组 A4:C28,如图 13-11 所示。 具体的执行步骤如下: • 选中单元 E5,按 F2 键,回车执行 Excel link 函数 MLPutMatrix("data",DATA),将 DATA 拷贝到 MATLAB 7.0 中。DATA 包含了对 3 个变量的 25 次观测值,并且已知观测值之 间具有强线性相关性; • 对 E8、E9 和 E10 执行相同的操作,在 Excel 中一次执行下列语句: MLEvalString("y = data(:,3)") MLEvalString("e = ones(length(data),1)") MLEvalString("A = [e data(:,1:2)]") 这些语句实现对回归目标数据的设置,即用第一列和第二列数据对第三列进行回归。变 量 y 用来存储第三列的数据,变量 A 的第一列为单位向量,后两列分别为原 data 的第一列和 第二列。 图 13-11 exliamp.xls 中的 sheet1 数据页 –495– MATLAB 7.0 • 执行 E13 的命令,代码设置如下: MLEvalString("beta = A\y") 利用 MATLAB 7.0 中的反除法计算 A*beta = y 的回归系数。 • 执行 E16 的命令,代码设置如下: MLEvalString("fit = A*beta") 利用 MATLAB 7.0 的矩阵向量乘法计算回归结果。 • 执行 E19、E20 和 E21 的命令,代码设置如下: MLEvalString("[y,k] = sort(y)") MLEvalString("fit = fit(k)") MLEvalString("n = size(data,1)") 这些语句实现原始数据和回归数据的比较,将数据按升序排列后进行拟合,生成对观察 数据的比例因子。 • 执行 E24 和 E25 的命令对 data 进行多项式拟合,代码设置如下: MLEvalString("[p,S] = polyfit(1:n,y’,5)") MLEvalString("newfit = polyval(p,1:n,S)") • 执行 E28 的命令,代码设置如下: MLEvalString("plot(1:n,y,’bo’,1:n,fit,’r:’,1:n,newfit,’g’); legend(’data’,’fit’,’newfit’)") 利用 MATLAB 7.0 绘图函数显示原始数据 data、拟合数据 fit 和多项式拟合数据 newfit, 如图 13-12 所示。 图 13-12 data、fit 和 newfit 的比较 对图 13-12 中 data、fit 和 newfit 三条曲线进行比较,可以发现数据具有很强的相关性, 不是线性独立的,拟合曲线和原始数据并不是十分吻合,而 5 阶多项式拟合则显示了更加精 确的数学模型。 13.5.2 宏命令执行模式(Macro Version) 单击 Sheet2 标签,可以运行本例的宏命令执行模式。 激活单元 A4,如图 13-13 所示,但先不要执行它。 –496– 第 13 章 MATLAB 7.0 与 Word、Excel 的混合使用 图 13-13 数据表 2 单元 A4 调用宏 CurveFit,可以在 Visual Basic 环境下打开 CurveFit。操作步骤如下: • 在 Excel 中执行工具菜单下“宏”选项中的“Visual Basic 编辑器”选项,启动 Visual Basic; • 在 Visual Basic 的工程里打开模块文件夹,如图 13-14 所示; 图 13-14 Visual Basic 的工程栏 • 选中 Moduel,则可以打开该模块,CurveFit 的程序代码如下: Function CurveFit(aData, sTarget1, sTarget2, sTarget3) ’MATLAB regression and curve fitting macro MLPutMatrix "data", aData MLEvalString "y = data(:,3)" MLEvalString "n = length(y)" MLEvalString "e = ones(n,1)" MLEvalString "A = [e data(:,1:2)]" MLEvalString "beta = A\y" MLEvalString "fit = A*beta" MLEvalString "[y,k] = sort(y)" MLEvalString "fit = fit(k)" MLEvalString "[p,S] = polyfit(1:n,y’,5)" MLEvalString "newfit = polyval(p,1:n,S)’" –497– MATLAB 7.0 MLEvalString "plot(1:n,y,’bo’,1:n,fit,’r:’,1:n,newfit,’g’);legend(’data’,’fit’,’newfit’)" MLGetMatrix "y", sTarget1 MLGetMatrix "fit", sTarget2 MLGetMatrix "newfit", sTarget3 End Function 在此模块为打开的状态下,在工具菜单栏中单击“引用”命令,在弹出的“引用”对话 框中查看是否选中了“Excellink”项,如图 13-15 所示,如果没有则需要加载。 图 13-15 “引用”对话框 返回到 Sheet2 的单元 A4,按 F2 回车,执行宏 CurveFit。宏 CurveFit 包含了工作表执行 模式的第 1 步到第 7 步的操作,并且将排序后的数据 y、fit 和 newfit 从 MATLAB 7.0 拷贝到 数据表中,如图 13-16 所示。 图 13-16 从 MATLAB 7.0 拷贝到数据表中的 y、fit 和 newfit 13.6 Excel link 使用的几个问题 为了更有效地利用 Excel link,下面对一些细节问题做进一步地说明。使用者必须牢记 Excel link 函数执行的是一个特定操作,而 Microsoft Excel 的函数返回一个确定的数值,所以 –498– 第 13 章 MATLAB 7.0 与 Word、Excel 的混合使用 Excel 的操作和函数在 Excel link 函数的控制下会有不同的表现。 13.6.1 关于语法 1.大小写的区分 Excel link 函数名对字母的大小写不作区分,例如 MLPutMatrix 和 mlptmatrix 代表同一函 数。而 MATLAB 7.0 函数名是区分大小写的,例如 BONDS、Bonds 和 bonds 就代表不同的变 量。标准的 MATLAB 7.0 函数名通常使用小写字母,比如 plot()。 2.等式的起始标记 Excel 工作表等式通常以“+”或“=”作为起始标记,例如=mlputmatrix("a",C10)。 3.变量的定义方式 在大多数 Excel link 函数中有两种定义变量的方式,即直接定义和间接定义。将变量用 双引号标记即可直接定义变量,例如 MLDeleteMatrix("Bonds"),函数中的不加双引号的工作 区单元地址或行列名称被视为间接变量,函数对其指引内容进行操作。工作区单元地址可以 包含页表序号。 13.6.2 关于工作表 1.工作表的单元值 Excel link 函数执行过程中其所在数据单元将一直显示其函数内容,函数执行完毕后,数 据单元将被赋值为 0。 2.数据输入模式的是指 建议读者检查 Excel 工具菜单里“选项”的“编辑”选项卡中“按 Enter 键后移动”选项, 如图 13-17 所示,以保证输入完毕经确认后再改变当前工作单元。 图 13-17 选择“按 Enter 键后移动”模式 –499– 第 14 章 编译工具箱 使用 MATLAB 7.0 的读者在感叹其运算功能强大的同时,往往希望程序可以运行的更快; 希望获得可以摆脱 MATLAB 7.0 环境而独立运行的可执行软件。而 MATLAB 7.0 推出的编译 器 Compiler 4.0 正是满足用户这些需求的软件。本章将重点向读者介绍 MATLAB 7.0 编译工 具箱的使用。 14.1 编译器概述 MATLAB 7.0 Compiler 4.0 是将 M 文件作为它的输入,而产生可以重新分配的,并且是 独立运行的应用程序或相应的软件组件。这些生成的应用程序以及软件组件都是与平台相关 的。MATLAB 7.0 编译器可以产生下面几种应用程序或软件组件: • 独立运行的程序:独立运行的程序顾名思义是在其运行的过程中可以不需要 MATLAB 7.0 软件的同时运行,甚至它们可以在没有安装 MATLAB 7.0 的机器上运行; • C 和 C++共享库(在 Windows 操作系统中为动态链接库 DLL):这些共享库可以在没 有安装 MATLAB 7.0 的用户机器上运行; • Excel 附件:需要 MATLAB 7.0 Builder; • COM 对象:需要 MATLAB 7.0 Builder。 本章着重介绍前面两种类型的应用程序,而对于后面两种软件附件,在下面的章节进行 相关介绍。 在进入正题之前,这里需要说明的是 MATLAB 7.0 编译器同 MATLAB 7.0 解释器之间的 区别。MATLAB 7.0 解释器是指可以执行 MATLAB 7.0 命令、可执行的 M 文件以及 MEX 文 件的应用程序,事实上,当我们使用 MATLAB 7.0 的时候,也就是在使用 MATLAB 7.0 的解 释器。而 MATLAB 7.0 编译器是指将 M 文件作为其输入,同时生成独立的可执行文件或相关 软件组件的程序,它可以由 mcc 命令调出。 14.2 编译器的安装和配置 14.2.1 配置 MATLAB 7.0 编译器的前提准备 MATLAB 7.0 在第一次使用其编译器时,将会自动对其进行适当的配置。然而,MATLAB 7.0 编译器的最初配置可以随时进行手动更改。下面将对此进行详细介绍。 1.安装 ANSI C/C++编译器。 第 14 章 编译工具箱 在用户的计算机上,应事先安装以下任何一种与 MATLAB 7.0 适配的 ANSI C/C++编 译器: • Lcc C:由 MATLAB 7.0 自带,只是一个 C 编译器,不能用来编译 C++; • Borland C++:版本为 5.3、5.4、5.5、5.6; • Microsoft Visual C/C++ (MSVC):版本为 6.0、7.0 和 7.1。 2.安装 MATLAB 7.0 Compiler。 MATLAB 7.0 Compiler 的安装过程一般包含在安装 MATLAB 7.0 之中,当用户选择 Typical 的安装模式时,MATLAB 7.0 Compiler 会被自动选为 MATLAB 7.0 的安装组件。当用 户选择 Custom 安装模式时,在默认情况下,MATLAB 7.0 Compiler 选项是被选中的,如图 14-1 所示。 图 14-1 MATLAB 7.0 安装选项图 14.2.2 对编译器进行配置 本节将向读者介绍如何配置 C 或 C++编译器,使其可以与 MATLAB 7.0 编译器一起进行 工作。在 MATLAB 7.0 中,函数命令 mbuild 可以大大地简化了配置 C 或 C++编译器的过程。 一般而言,用户仅仅需要使用 mbuild 命令中的 setup 选项就可以轻松的设置好第三方编译器。 说明:在很多数的情况下,当用户将第三方编译器安装到其默认路径下时,是不需要运行 mbuild –setup 命令的。 当选用一个同 MATLAB 7.0 编译器相关联的 C 或 C++编译器时,可以使用命令 mbuild, 同时加上参数-setup,代码设置如下: mbuild –setup –501– MATLAB 7.0 从入门到精通 命令执行结果代码如下: Please choose your compiler for building standalone MATLAB applications: Would you like mbuild to locate installed compilers [y]/n? n 选择 n,从而手动选择编译器。 Select a compiler: [1] Borland C++Builder version 6.0 [2] Borland C++Builder version 5.0 [3] Borland C++Builder version 4.0 [4] Borland C++Builder version 3.0 [5] Borland C/C++ version 5.02 [6] Borland C/C++ version 5.0 [7] Borland C/C++ (free command line tools) version 5.5 [8] Lcc C version 2.4 [9] Microsoft Visual C/C++ version 7.1 [10] Microsoft Visual C/C++ version 7.0 [11] Microsoft Visual C/C++ version 6.0 [0] None Compiler: 11 这里选择 11,即 Microsoft Visual C/C++ version 6.0。 Your machine has a Microsoft Visual C/C++ compiler located at D:\Microsoft Visual Studio. Do you want to use this compiler [y]/n? y 选择 y,表示接受当前编译器的选择。 Please verify your choices: Compiler: Microsoft Visual C/C++ 6.0 Location: D:\Applications\Microsoft Visual Studio Are these correct?([y]/n): y 再次确认编译器的选择。 Try to update options file: C:\WINNT\Profiles\username\Application Data\MathWorks\MATLAB\R14\compopts.bat From template: \\sys\MATLAB\BIN\WIN32\mbuildopts\msvc60compp.bat Done . . . . Updated –502– 14.3 MATLAB 7.0 编译器的使用 第 14 章 编译工具箱 14.3.1 编译过程 MATLAB 7.0 编译器 4.0 版本采用了 MATLAB 7.0 Component Runtime(MCR)技术,它 是一组用来保证 M 文件执行的独立的共享库。MCR 提供了对 MATLAB 7.0 语言的完全支持。 除此之外,MATLAB 7.0 编译器 4.0 版本还采用了 Component Technology File(CTF)存档来 组织配置文件包。所有的 M 文件均采用了高级加密标准(AES)进行了密钥为 1024 位的加 密,保存位 CTF 格式。每一个由 MATLAB 7.0 编译器生成的应用程序或者共享库均有一个与 之相对应的 CTF 存档,其中包括了所有的基于 MATLAB 7.0 的 M 文件、MEX 文件等。 在 MATLAB 7.0 编译器中,生成独立文件或软件组件的过程是完全自动的。例如,为了 生成一个独立运行的 MATLAB 7.0 应用程序,用户只需要提供一系列用来构成应用程序的 M 文件,然后编译器将会自动执行以下操作: • 依赖性分析:分析判断输入的 M 文件、MEX 文件以及 P 文件所依赖的函数之间的关 系,其中这些函数包括了输入文件所调用的 M 文件以及 MATLAB 7.0 提供的函数; • 代码生成:生成所有用来生成目标组件的代码,其中包括:与从命令行中获得的 M 函 数相关的 C 或 C++接口代码,对于共享库和组件来说,这些代码还包括了所有的接口 函数;组件数据文件,其中包含了运行时执行 M 代码的相关信息,这些信息中有路径 信息以及用来载入 CTF 存档中 M 代码的密钥; • 存档生成:在依赖型分析中生成的 MATLAB 7.0 可执行程序列表被用来生成 CTF 存档 文件,其中包括程序运行时所需组件的数据。存档在加密后被压缩位一个单独的文件, 同时路径信息也被保存; • 编译:编译生成的 C 或 C++文件,得到目标代码。对于那些含有用户提供 C 或 C++ 代码的目标,这些代码同样会被编译; • 链接:将生成的目标文件以及相关的 MATLAB 7.0 库链接起来,并生成最终的组件。 14.3.2 MCR 的安装 为了能够使用 MATLAB 7.0 编译器生成的组件,用户的机器上必须装有 MCR。下面介 绍其具体的过程。 首 先 在 MATLAB 7.0 根 目 录 下 的 \toolbox\compiler\deploy\win32 目 录 下 , 将 MCRInstaller.exe 拷贝到其他路径,然后双击进行安装,如图 14-2 所示。 –503– MATLAB 7.0 从入门到精通 图 14-2 MCR 安装图示 然后选择安装路径,如图 14-3 所示。 图 14-3 选择 MCR 安装路径 按照提示继续安装,直到提示安装结束。 14.3.3 编译指令 mcc 不管是想生成独立外部可执行程序,或者是想创建一个 C 共享库以及软件组件,只要源 码是 M 文件,那么都可以借助编译命令 mcc 实现。用户可以在 MATLAB 7.0 环境中以及 DOS 或 UNIX 命令行环境中使用 mcc 指令。 用户可以声明一种或多种 MATLAB 7.0 编译器参数给 mcc 指令。大部分参数的名字由一 个字母组成,用户可以独立地使用两个参数,如下面地示例代码: mcc -m -g myfun Macros 是 MathWorks 公式提供的用来简化编译任务编译参数,它使用户在面对一个简单 问题的时候可以不再亲自手工组合多种参数来实现某种特定的编译过程,而是仅仅使用参数 -m 即可。 –504– 第 14 章 编译工具箱 有了 Macros 参数,在处理大部分的编译问题时,我们均可以简单地键入下面格式的编 译命令: mcc -m myfun 14.3.4 创建独立的应用程序 为了说明编译的具体过程,下面举两个例子,分别代表不同类型的编译过程。 1.M 文件 建立一个脱离 MATLAB 7.0 环境,并可独立运行的外部程序。该程序的功能是对于给定 矩阵 A,如果存在 S 使得 S-1AS = Λ,则要求出一个 S,否则给出信息说明所给的矩阵不能对 角化。 首先编写两个 M 函数文件,即 exm2.m 和 exm2_f.m(第一个文件是主文件)。 exm2.m 的代码如下: function exm2 A = [4,0,0;0,3,1;0,1,3]; S = exm2_f(A) exm2_f.m 的代码如下: function S = exm2_f(A) [m,n] = size(A); if m ~= n error(’输入矩阵应是方阵’); end e = eig(A); %检查输入矩阵的特征值是否各异 same = 0; for i = 1:m-1 for j = (i+1):m if e(j) == e(i) same = 1; end end end %A 可以对角化的条件是 A 具有各异特征值或者 A 位埃米尔特矩阵。 if any(any((A’-A)))&(same ==1) error(’矩阵无法对角化’); end [v,d] = eig(A); S = v; 把这两个函数文件保存在在用户自己的目录,例如 d:\matlab7\work,并在 MATLAB 7.0 –505– MATLAB 7.0 从入门到精通 中运行检验,代码设置如下: exm2 得到结果如下: S= 0 0 1.0000 -0.7071 0.7071 0 0.7071 0.7071 0 生成独立的外部可执行程序。在 MATLAB 7.0 指令窗口中,运行如下指令: mcc -m exm2 exm2_f 打开 DOS 窗口,在 d:\matlab7\work 目录下,运行 exm2.exe,得到结果如图 14-4 所示。 图 14-4 在 DOS 窗口运行生成程序 exm2.exe 所得的结果 2.M 文件和 C 文件混合 本例给出了如何在 C 语言文件中调用编译过的 M 函数。考虑一个由两部分组成的程序: • multarg.m:其中包含一个函数 multarg; • multargp.c:其中包含一个 C 语言函数 main。 multarg.m 中包含的 multarg 函数包含两个输入参数和两个输出参数。源代码如下: function [a,b] = multarg(x,y) a = (x + y) * pi; b = svd(svd(a)); multargp.c 中的 main 函数调用 multarg.m 文件中的 multarg 函数,并显示 multarg 函数的 返回值。源代码如下: #include #include #include #include "libMultpkg.h" /* –506– * Function prototype; the MATLAB 7.0 Compiler creates mlfMultarg * from multarg.m */ void PrintHandler( const char *text ) { printf(text); } /* main 函数用来调用 multarg 函数 */ int main( ) { #define ROWS 3 #define COLS 3 mclOutputHandlerFcn PrintHandler; mxArray *a = NULL, *b = NULL, *x, *y; double x_pr[ROWS * COLS] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; double x_pi[ROWS * COLS] = {9, 2, 3, 4, 5, 6, 7, 8, 1}; double y_pr[ROWS * COLS] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; double y_pi[ROWS * COLS] = {2, 9, 3, 4, 5, 6, 7, 1, 8}; double *a_pr, *a_pi, value_of_scalar_b; /* 初始化图形句柄,同时声明显示格式 */ mclInitializeApplication(NULL,0); libMultpkgInitializeWithHandlers(PrintHandler, PrintHandler); /* 生成输入矩阵 "x" */ x = mxCreateDoubleMatrix(ROWS, COLS, mxCOMPLEX); memcpy(mxGetPr(x), x_pr, ROWS * COLS * sizeof(double)); memcpy(mxGetPi(x), x_pi, ROWS * COLS * sizeof(double)); /*生成输入矩阵 "y" */ y = mxCreateDoubleMatrix(ROWS, COLS, mxCOMPLEX); memcpy(mxGetPr(y), y_pr, ROWS * COLS * sizeof(double)); memcpy(mxGetPi(y), y_pi, ROWS * COLS * sizeof(double)); /* 调用 mlfMultarg 函数. */ mlfMultarg(2, &a, &b, x, y); /* 显示得到的矩阵 "a". */ 第 14 章 编译工具箱 –507– MATLAB 7.0 从入门到精通 mlfPrintmatrix(a); /* 显示输出矩阵 "b" */ mlfPrintmatrix(b); /* 销毁中间矩阵变量. */ mxDestroyArray(a); mxDestroyArray(b); libMultpkgTerminate(); mclTerminateApplication(); return(0); } 将上述两个程序分别保存到用户工作目录下,在 MATLAB 7.0 的运行环境中,对两个文 件进行编译链接。 mcc -W lib:libMultpkg -T link:exe multarg printmatrix multargp.c 可以得到运行结果如图 14-5 所示。 图 14-5 混合源码运行结果 3.包含绘图指令的 M 文件 由于安装了 MCR,因此在编译的过程中可以加入 MATLAB 7.0 图形库。 定义 M 文件 test_plot.m,源代码如下: function test_plot t = 0:pi/50:10*pi; plot3(sin(t),cos(t),t) axis square; grid on 在 MATLAB 7.0 命令窗口中输入如下代码: mcc -m test_plot.m –508– 第 14 章 编译工具箱 得到编译链接后的文件 test_plot.exe。双击执行,可以得到结果如图 14-6 所示。 图 14-6 带绘图指令的 M 文件编译运行结果 4.由含 feval 指令的 M 文件生成 EXE 文件 feval 指令的特殊之处在于它的第一个输入参量是函数名字符串。这里要讨论的是当 M 源文件包含该指令时,将如何从这个源文件获得 EXE 文件。在 MATLAB 7.0 中给出了编译 注记(Pragmas)处理 feval 对函数的调用。其格式为: %#function 编译注记提示 MATLAB 7.0 编译器在编译的过程中需要将函数列表(function_name-list) 中的函数均包含进去,而无论编译器的依赖性分析过程中是否定位到函数列表中的函数。当 然,用户也不可将编译注记指向不存在的函数。 下面是一个采用编译注记的例子,由两个函数组成 mywindow.m 和 test1.m。其中 test1 用来调用 mywindow.m 文件。 mywindow.m 文件的内容为: function ret = mywindow(data,fitlerName) %对输入的数据进行加窗 %获得输入数据的长度 N= length(data); %列出所有的窗函数 %#function bartlett, barthannwin, blackman,blackmanharris,bohmanwin,chebwin, flattopwin, gausswin, hamming,hann,kaiser, nuttallwin,parzenwin, rectwin, tukeywin,triang window = feval(fitlerName,N);% 对输入的数据进行加窗. –509– MATLAB 7.0 从入门到精通 ret = data.*window; test1.m 文件的内容为: function test1 data = sin((1:10)/10*pi)’; newdata = mywindow(data,’chebwin’); disp(newdata’) 对主文件进行编译,可以产生符合要求的独立可执行程序 test1.exe。 mcc -m test1.m 程序执行结果如图 14-7 所示。 图 14-7 含 feval 指令的 M 文件编译运行结果 –510– 第 15 章 应用程序接口 MATLAB 虽然是一个完整的科学计算与可视化的环境,但在很多情况下它仍需与其他外 部程序沟通,可用 MATLAB 提供的应用程序接口(Application Program Interface,简称 API) 来实现此功能。 MATLAB 应用程序接口包括 3 部分内容,分别为 MEX 文件——外部程序调用接口,在 MATLAB 中调用 C 语言或者 Fortran 语言编写的程序,以提高数据处理的效率;MAT 文件应 用程序——数据输入输出接口,向 MATLAB 环境传送数据或从 MATLAB 环境接收数据,即 实现 MATLAB 系统与外部环境的数据交换;MATLAB 计算引擎函数库——在 MATLAB 和 其他应用程序间建立客户机/服务器关系,将 MATLAB 作为一个计算引擎,在其他应用程序 中调用,从而降低程序设计的工作量。MATLAB 在 Windows 平台上支持 Microsoft 提出的 COM 标准,同时支持 Java 语言,所以 MATLAB 几乎可以同 Windows 平台上任何一种软件 或者开发语言进行交互。 本章将重点讨论这些应用程序接口的实现方法。 15.1 创建 C 语言 MEX 文件 本节将介绍在 MATLAB 中创建 C 语言的 MEX 文件的方法,在这之前,首先对 MEX 文 件作一个简要的介绍。 15.1.1 MEX 文件简介 MEX 从字面上是 MATLAB 和 Executable 两个单词的缩写。一般 MEX 文件使用 C 语言 或者 Fortran 语言进行开发,通过适当的编译后,生成的目标文件能够被 M 语言解释器调 用执行,这种文件在 Microsoft Windows 下使用后缀.dll。MEX 文件重要应用于已存在较大 规模的 C 和 Fortran 程序,可以比较容易的在 MATLAB 中调用或是在 MATLAB 中运行不 很有效,存在计算瓶颈或是直接面向硬件编写的 C 和 Fortran 程序可以通过 MEX 文件被 MATLAB 调用。 “阵列”是 MATLAB 惟一能处理的对象,在 C 语言中,MATLAB“阵列”用结构体 mxArray 定义。 下面通过一个最简单的 MEX 文件示例来认识 MEX 源文件的结构。 【例 15.1】简单 MEX 文件示例——mexhello.c 示例代码设置如下: /*necessary header file*/ #include "mex.h" MATLAB 7.0 /*entrance function of MEX function*/ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[]) { /*content of function ,transfer other function*/ mexPrintf("Hello world!"); } 这是标准的 C 语言源文件,其程序的基本语法完全是 ANSI C 的语法结构。首先是头文 件的包含语句,所有的源文件中必须包含 mex.h,它完成了所有 C 语言 MEX 函数的原型声 明,还包含了 matrix.h 文件,即对 mx 函数和数据类型的声明和定义。 接下来是 C 语言 MEX 源文件的入口函数部分,这也是必需的部分,并且书写形式固定。 括号里的 4 个变量分别表示输入参数的个数、输入参数、输出参数的个数和输出参数。 需要注意的是,MATLAB 7.0 的 MEX 源文件中不能包括任何汉字,否则会出现编译连 接错误。 上述 MEX 源文件编写完毕后,保存在 MATLAB 的当前工作路径下,名称为 mexhello.c, 接着在命令窗口键入以下指令: >> mex mexhello.c 此命令对 MEX 文件进行编译,创建出 MATLAB 的 MEX 文件,可以在 MATLAB 命令 行中运行下面的语句: >> mexhello Hello world! 注意:上述编译过程也可以在命令行提示符窗口中完成。 需要注意的是 MATLAB 本身是一个高效的系统,编程简单,效率高,不到万不得已, 建议不要使用 MEX 手段。 15.1.2 编写 C MEX 文件 编写 MEX 文件源程序时,要用到两类 API 库函数,即 mx-库函数和 mex-库函数,前者 用于在 C 语言中创建、访问、操作和删除结构体 mxArray,后者用于与 MATLAB 环境进行 交互。有关这些库函数的详细说明可参看 MATLAB 的 help 文件。 下面通过一个实例来演示 C MEX 的编写过程。 【例 15.2】生成随机数——执行 MATLAB 函数 示例代码设置如下: /*MEX function example,create random numbers——randomnum.c*/ #include "mex.h" /*MEX function entrance*/ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[]) { double *m,*n,*flag; /*error detecting*/ –512– 第 15 章 应用程序接口 if(nlhs>1||nrhs!=3) mexErrMsgTxt("error:wrong numbers of input/output!"); m=mxGetPr(prhs[0]); n=mxGetPr(prhs[1]); flag=mxGetPr(prhs[2]); plhs[0]=mxCreateDoubleMatrix(*m,*n,mxREAL); /*set output flag*/ mexSetTrapFlag(*flag); /*wrong transfer-->function rnd does not exsit*/ mexCallMATLAB(1,plhs,2,prhs,"rnd"); /*continue to run MEX function file so rand function is transfered by MEX file*/ mexPrintf("user set Flag to 1.\n"); /*rright transfer*/ mexCallMATLAB(1,plhs,2,prhs,"rand"); } 本例的源程序中使用 mexCallMATLAB 函数调用了 MATLAB 的随机数产生函数 rand。 mexCallMATLAB 函数的定义如下: int mexCallMATLAB(int nlhs,mxArray *plhs[],int nrhs,msArray *prhs[],const char *command_name); 其中前 4 个参数为需要输入到被调用函数中的输入、输出参数,而最后一个参数为被调 用的函数名称。当执行 command_name 函数出现错误时,根据 mexSetTrapFlag 函数的设置进 行错误处理,其定义如下: void mexSetTrapFlag(int trap_flag); 若输入参数为 0,则将程序的控制权交给 MATLAB,退出 MEX 函数的执行,若输入参 数为 1,则将程序的控制权交给 MEX 函数,继续执行 MEX 函数。例子中的第一次 MATLAB 调用是错误的,不存在 rnd 函数,程序如何执行将决定于 flag 值的设定。 编译并执行 randomnum.c,代码设置如下: >> mex randomnum.c >> y=randomnum(3,2,0) %flag 为 0 ??? Undefined command/function 'rnd'. %调用不成功,再次运行,flag 设为 1 >> y=randomnum(3,2,1) ??? Undefined command/function 'rnd'. user set Flag to 1. y= 0.9501 0.4860 0.2311 0.8913 0.6068 0.7621 不同的实例将会用到不同的 mex 函数,有关其他 mex 函数的使用,用户可以参看 help 说明和其他专门资料的例程。 此外,MEX 文件还可用 Visual Studio 来创建,适合习惯利用集成开发环境的程序员使用。 –513– MATLAB 7.0 15.2 创建 Fortran 语言 MEX 文件 使用 Fortran 语言创建 MEX 文件的方法和过程与创建 C 语言 MEX 文件的方法和过程几 乎一样,无非是使用的高级编程语言有所不同而已。Fortran 语言在科学计算方面的应用比 C 语言要广泛,它的数值稳定性和多种数值变量是 C 语言无法匹敌的。 本节主要介绍 Fortran 语言 MEX 文件的创建。 15.2.1 Fortran 语言 MEX 文件简介 以一个简单的例程介绍 Fortran 语言 MEX 文件。 【例 15.3】简单的 Fortran 语言 MEX 文件——mexhello.c C entrance function subroutine mexFunction(nlhs,plhs,nrhs,prhs) C -----------------------------------------C parameters defination integer plhs(*),prhs(*) integer nlhs,nrhs C code call mexPrintf('Hello MATLAB World!') C end of file return end 它的功能也是在 MATLAB 命令窗口输出一段文字。在 M 文件编辑器中输入完毕后,保 存在当前工作目录下,名称为 mexhello.f。然后编译运行: >> mex mexhello.f >> mexhello Hello MATLAB World! 注意:在进行编译之前,一定要在 MATLAB 环境下完成 MEX 文件编译器的配置,本书例程中所 使用的 Fortran 开发环境是 Compaq Visual Fortran。 与 C MEX 类似,首先是入口函数的声明部分(没有了头文件的包含语句),输入参数和 输出参数的含义与 C 相同,然后是源程序的主体部分,进行数据的存取和内存的分配,调用 相应的计算子程序或者函数。程序的结尾是 Fortran 的特别要求,必须有 return 和 end 关键字 作为结束。 MATLAB 的数据在 C 中使用 mxArray 数据类型来表示,但是在 Fortran 中没有显性的定 义该数据结构,而是通过一种所谓的“指针”类型数据完成 Fortran 和 MATLAB 之间的数据 传递。MATLAB 将需要传递的 mxArray 数据指针保存为一个整数类型的变量,例如在 mexFunction 入口函数中声明的 prhs 和 plhs,然后在 Fortran 程序中,通过能够访问指针的 Fortran 语言 mx 函数访问 mxArray 数据,获取其中的实际数据。 –514– 15.2.2 Fortran MEX 文件示例 【例 15.4】创建稀疏矩阵 C fcreatesparse.f C create sparse array C entrance function C parameters declaration integer plhs(*),prhs(*) integer nlhs,brhs C pointer integer ir,jc,pr C actual data real*8 pdata(6) data pdata/3,1,10,-7,2,-2/ integer irdata(6) data irdata/0,3,3,1,2,3/ integer jcdata(5) data jcdata/0,2,2,5,6/ 第 15 章 应用程序接口 integer mxGetIr,mxGetJc,mxGetPr integer mxCreateSparse integer mxCopyReal8ToPtr integer mxCopyInteger4ToPtr C code plhs(1)=mxCreateSparse(4,4,6,0) ir=mxGetIr(plhs(1)) jc=mxGetJc(plhs(1)) pr=mxGetPr(plhs(1)) C memory copy call mxCopyReal8ToPtr(pdata,pr,6) call mxCopyInteger4ToPtr(irdata,ir,6) call mxCopyInteger4ToPtr(jcdata,jc,5) C end of file return end 本例的核心部分首先使用 mxCreateSparse 函数创建了稀疏矩阵的 mxArray 数据对象,然 后分别使用 mxGetIr、mxGetJc、mxGetPr 3 个函数获取稀疏矩阵的 3 个重要指针 ir、jc 和 pr, 这里是实数的矩阵,所以没有 pi 指针。接着使用 mxCopy 函数完成数据内存的复制。需要注 意的是整数类型的数据拷贝,在 MATLAB 的 API 函数库中,有 3 个不同的函数用于整数类 型数据的拷贝,分别是 mxCopyInteger1ToPtr、mxCopyInteger2ToPtr 和 mxCopyInteger4ToPtr。 –515– MATLAB 7.0 同样,还有 3 个函数用于将指针数据拷贝给相应的整型数据,这里不再列举。具体程序编 写过程中要针对具体的整数数据类型选取不同的拷贝函数。下面是编译运行该文件后的一 些结果: >> mex fcreatesparse.f >>A=fcreatesparse A= (1,1) 3 (4,1) 1 (1,3) 10 (2,3) -7 (3,3) 2 (4,4) -2 >>full(A) ans= 3 0 10 0 0 0 -7 0 002 0 1 0 0 -2 15.3 MAT 文件应用 MATLAB 与外界交互数据有直接输入输出法(用于数据量小的情况)、数据的 M 文件输 入和 diary 文件输出法、输入输出数据的 ASCII 文件法、低层 IO 指令读写数据法、读写数据 的 MEX 文件法和 MAT 文件法等多种方法。其中 MAT 文件法是先用 C 或者 Fortran 编写一 个专门的文件,实现原数据与 MAT 文件格式之间的相互转换,进而借助 save,load 实现 MATLAB 对原数据的读写。MAT 文件是 MATLAB 数据存储的默认文件格式,由文件头和数 据组成,文件扩展名是.mat。 【例 15.5】save 和 load 指令示例 在 MATLAB 命令行窗口中,键入下面的指令: >> clear all >> %create variables >> x1=2;x2=3;x3=4;y1=0; >> %save data >> save xdata x1 x2 >> %check MAT file in current directory >> dir *.mat xdata.mat >> clear all >> %load data(binary) –516– 第 15 章 应用程序接口 >> load xdata >> whos Name Size Bytes Class x1 1x1 8 double array x2 1x1 8 double array Grand total is 2 elements using 16 bytes 下面将介绍 MAT 文件的应用,所谓 MAT 文件的应用就是指读写 MAT 数据文件的 C 语 言或者 Fortran 语言应用程序,它可以是 MEX 文件也可以是独立的可执行应用程序。为了便 于读写 MAT 文件,MATLAB 提供了响应的接口函数——mat 函数,MAT 文件应用程序就是 利用这些 mat 函数完成 MAT 数据文件的读写工作。下面给出一个 C 语言 MAT 文件应用的例 子,可从中了解 MAT 文件应用程序的基本结构和 MAT 文件应用的基本过程。 【例 15.6】在 C 语言 MEX 文件中创建 MAT 数据文件——mexcreatematfile.c 示例代码设置如下: /*necessory header file*/ #include "mat.h" #include "math.h" /*MEX function file entrance*/ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[]) { mxArray *string,*scalar,*array; double img[]={0,1,2,3,4,5,6,7,8}; double real[]={8,7,6,5,4,3,2,1,0}; double *pr,*pi; mxChar *filename; MATFile *file; int buflen=0; /*judge the input/output*/ if(nrhs!=1) mexErrMsgTxt("an input is necessory to be the filename!"); if(mxGetClassID(prhs[0])!=mxCHAR_CLASS) mexErrMsgTxt("input must be characters, stand for filename!"); /*create data*/ string=mxCreateString("MATLAB is wonderful!"); scalar=mxCreateDoubleScalar(2003); array=mxCreateDoubleMatrix(3,3,mxCOMPLEX); pr=mxGetPr(array); pi=mxGetPi(array); memcpy(pr,real,9*sizeof(double)); memcpy(pr,img,9*sizeof(double)); /*get the filename*/ –517– MATLAB 7.0 buflen=mxGetNumberOfElements(prhs[0])+1; filename=mxMalloc(buflen); mxGetString(prhs[0],filename,buflen); /*create new MAT file*/ file=matOpen(filename,"w"); if(file==NULL) mexErrMsgTxt("can't create appointed file"); /*write data to MAT file*/ matPutVariable(file,"String",string); matPutVariable(file,"Scalar",scalar); matPutVariable(file,"Array",array); /*close MAT file*/ matClose(file); mexPrintf("successfully create MAT file:%s\n",filename); /*release the memory*/ mxDestroyArray(string); mxDestroyArray(scalar); mxDestroyArray(array); mxFree(filename); } 首先所有 MAT 数据文件应用程序的 C 语言代码中必须包含 mat.h,它定义了 mat 函数的 原型,并且包含了 matrix.h 头文件。接下来的代码和普通的 MEX 文件应用程序的代码没什 么区别,然后用 matOpen 函数打开一个数据文件,再调用 matPutVariable 函数向 MAT 文件中 写入数据。最后关闭 MAT 文件(matClose)并释放内存。编译链接生成 MEX 文件,然后运 行以下代码: >> clear all >> mex mexcreatematfile.c >> mexcreatematfile('matdemo.mat') successfully create MAT file:matdemo.mat >> %查看生成了什么 >> what MAT-files in the current directory d:\MATLAB\work matdemo MEX-files in the current directory d:\MATLAB\work mexcreatematfile >> %加载数据文件 >> load matdemo >> whos Name Size Bytes Class Array 3x3 144 double array (complex) –518– 第 15 章 应用程序接口 Scalar 1x1 8 double array String 1x20 40 char array Grand total is 30 elements using 192 bytes >> Array Array = 036 147 258 >> Scalar Scalar = 2003 >> String String = MATLAB is wonderful! 有关 mat 函数的详细说明可以参阅 MATLAB 的 help 文档,由于篇幅限制,不再讨论 Fortran 语言 MAT 文件的应用和集成开发环境下的应用。 15.4 MATLAB 引擎技术的应用 MATLAB 计算引擎应用程序的思想和 MATLAB 的 MEX 文件正好相反,MEX 文件是在 MATLAB 环境下调用 C 或者 Fortran 语言程序的手段,而 MATLAB 计算引擎则是在 C 或者 Fortran 语言环境中调用 MATLAB 函数的方法。引擎函数本身用 C 或 Fortran 编写,在 Windows 平台上,它与 MATLAB 的通信是借助 ActiveX 进行的,它可运用于以下场合: • MATLAB 在其他语言编制的应用程序中被当作数学库程序调用,充分利用 MATLAB 指令简单、计算可靠的优点。 • MATLAB 在专用系统中被当作计算引擎时用,前台是 C 语言等编写的 GUI 图形用户 接口,后台由 MATLAB 执行各种复杂的计算分析,从而大大缩短了用户的开发时间。 本节主要介绍如何调用 MATLAB 引擎函数,如何用 C 语言编写调用 MATLAB 引擎的源 程序。至于如何编写 Fortran 语言源程序,可参阅 help 文档。 【例 15.7】简单的 C 语言计算引擎应用示例——simpleeng.c 通过本例的程序,可以了解调用 MATLAB 引擎的源程序的一般结构;引擎函数的用法, 这些函数都是以 eng 前缀开始的;MATLAB 环境外数据与引擎函数的配合使用。 • 编写源程序 simpleeng.c 示例代码设置如下: /*necessory header file*/ #include "engine.h" #include "stdio.h" #include "conio.h" #define BUFFERLEN 256 –519– MATLAB 7.0 /*main function*/ void main() { Engine *ep; char cmd[BUFFERLEN]; int i=0; int status=0; /*open the engine*/ ep=engOpen(NULL); if(ep==(Engine*)NULL){ printf("error:can't open MATLAB engine\n"); exit(-1); } /*execute MATLAB command*/ engEvalString(ep,"A=zeros(1,10);"); /*wait for a moment...*/ printf("\n please check the outcome in the MATLAB command line!\n"); printf("\n press any key to continue...\n"); getch(); /*execute other MATLAB commands*/ for(i=10;i<20;i++){ /*the command to be executed in MATLAB*/ sprintf(cmd,"A(%d)=fibonacci(%d);",i-9,i); /*execute the command*/ engEvalString(ep,cmd); } /*wait for a moment...*/ printf("\n please check the outcome in the MATLAB command line!\n"); printf("\n press any key to continue...\n"); getch(); /*close the MATLAB engine and release memory*/ status=engClose(ep); if(status!=0){ printf("can't close the engine\n"); exit(-1); } printf("\n MATLAB engine is successfully used.\n"); } 首先在代码的最开始包含了需要的头文件,在所有的 C 语言引擎应用程序中,都必须包 含 engine.h 头文件,它声明了所有 eng 函数的原型,并且包含了 matrix.h 头文件,其他的头 –520– 第 15 章 应用程序接口 文件根据需要包含进去;然后声明了 Engine 类型的指针,该指针类似于打开文件时的文件指 针,相当于计算引擎的接口句柄,有了这个指针就可以在 C 语言中执行 MATLAB 的指令了; 最后完成代码后应该关闭计算引擎,通过 engClose 函数完成,它关闭指针释放内存。 • 编译连接源程序 采用类似于上一节编译 MAT 数据文件引用程序的方法来编译计算引擎程序,可以在 MATLAB 的命令窗口下,也可以在命令提示符窗口下进行。和生成 MEX 文件不同的是,生 成 MEX 文件时,只要在 MATLAB 中完成编译器的配置工作就可以使用 MEX 指令完成编译, 因为在配置编译器的时候需要将编译器的选项文件改名为 mexopts.bat 选项文件,并且拷贝到 了系统环境目录下。但是在系统路径下 mexopts.bat 文件不能完成 MATLAB 计算引擎引用程 序的编译,必须通过 MEX 命令行指定具体的选项文件才可以。代码设置如下: >> mex -f D:\MATLAB7\bin\win32\mexopts\msvc70engmatopts.bat simpleeng.c • 运行 simpleeng.exe 可以直接双击 simpleeng.exe 文件,也可以在 MATLAB 命令行窗口键入以下指令: >> !simpleeng& 这时操作系统启动一个 MATLAB 进程(只包含 MATLAB 的命令行窗口),如图 15-1 所示。 图 15-1 计算引擎启动的 MATLAB 会话窗口 这时在 Windows 的命令行提示符中的显示如图 15-2 所示。 图 15-2 Windows 命令行提示符窗口 如果在图 15-1 所示的窗口中键入下面的指令: ? whos Name Size Bytes Class A 1x10 80 double array Grand total is 10 elements using 80 bytes ?A A= –521– MATLAB 7.0 0000000000 在命令提示符行中按任意键继续执行,重复出现提示信息后,回到新启动的 MATLAB 会话窗口,键入下列指令: ?A A= Columns 1 through 6 34 35 89 144 233 377 Columns 7 through 10 610 987 1597 2584 可见由于代码的执行 A 变成了 Fibonacci 数列的部分元素。然后再按任意键,则执行关 闭引擎的命令,MATLAB 会话自动退出运行。由上例可以看出,MATLAB 计算引擎应用程 序的基本工作流程如下: 打 开 计 算 引 擎 ( engOpen ) → 设 置 数 据 ( engPutVariable ) → 执 行 MATLAB 指 令 (engEvalString)→获取计算结果(engGetVariable)→关闭计算引擎(engClose)。 有关 eng 函数的详细说明可以参看 help 文档。 15.5 MATLAB 的 Java 接口 Java 语言是一种流行的面向对象的高级编程语言,能够完成各种类型的应用程序开发, MATLAB 和 Java 之间的关系是非常密切的,从 5.3 版本开始,MATLAB 中都包含了 Java 虚 拟机。在 MATLAB 中可以直接调用 Java 的应用程序,新版本的 MATLAB 工具箱中就包含了 很多使用 Java 语言开发的图形用户界面工具,例如 COM Builder 的用户界面 comtool 等。利 用 MATLAB 的 Java 接口可以完成下列工作:调用 Java API 类和包,完成核心功能;调用第 三方 Java 类;在 MATLAB 环境下创建 Java 对象;通过 Java 语法或者 MATLAB 语法使用 Java 对象的方法;在 Java 对象和 MATLAB 之间交互数据。 Java 可以填补 MATLAB 的一些功能上的空白,特别由于 Java 自身的优势,完全可以通 过 Java 语言获取大量的来自互联网或者数据库的数据,而 MATLAB 自身的优势是进行数据 分析、科学计算,两者有机结合,充分发挥各自的优势,将极大提高工作效率,节约开发时 间和成本。 在 MATLAB 中键入下列指令可以查看当前 MATLAB 使用的 Java 虚拟机的版本: >> version -java ans = Java 1.4.2 with Sun Microsystems Inc. Java HotSpot(TM) Client VM (mixed mode) 可根据此版本选择 JDK(Java 开发包)的版本,避免出现不兼容的现象。 15.5.1 Java 接口应用 Java 是一种面向对象的高级编程语言,其中类和对象是最基本的概念,如果要创建对象, –522– 第 15 章 应用程序接口 则必须有相应的类存在。Java 语言除了最基本的关键字以外,还提供了大量的预先编制好的 类,应用 Java 就是通过 Java 语言的类完成各种功能。一般可将这些类分为 3 种:Java 内建 类(由 Java 语言本身提供的类和类包);第三方定义类(一些商业化或者免费的应用于专门 领域的类包);用户自定义类(从已有的类派生出来或者直接开发的新类)。 不同的类组合在一起构成类包,这些类包一般安装在系统路径下面,通过系统变量 classpath 定义给操作系统。在 MATALB 中,其系统路径下存在一个文本文件——classpath.txt, 该 文 件 定 义 了 MATLAB 环 境 可 以 直 接 引 入 的 MATLAB 包 。 一 般 该 文 件 位 于 %MATLABROOT%\toolbox\local 路径下。默认情况下,classpath 文件只是定义了 Java 内建类 包和由 Mathworks 公司提供的工具箱类包,如果需要使用第三方或者用户自定义类包,则需 在文件中添加,其使用规则是:用户自定义类库添加直到父目录的完整路径,包添加直到最 高级父目录的完整路径,JAR 文件中的类库添加完整的路径、完整的文件名。引入 Java 类包 后就可直接创建 Java 对象。 在 MATLAB 中创建 Java 对象有直接用 Java 类定义或用 javaObject 函数创建两种方法。 下面举例说明。 【例 15.8】在 MATLAB 中键入下列指令 示例代码设置如下: >> %直接创建 >> fa=java.awt.Frame('Frame A'); >> %用 javaObject 方法 >> fb=javaObject('java.awt.Frame','Frame B'); >> whos Name Size Bytes Class fa 1x1 java.awt.Frame fb 1x1 java.awt.Frame Grand total is 2 elements using 0 bytes 在上述代码中,都使用了完整的 Java 类的名称,可以在 Java 语言中使用 import 命令导 入必要的类包,这样声明对象的时候就不用给出完整的类的名称了,在 M 语言中也是如此, 如上例,也可写作下面的代码: >> import java.awt.Frame >> fa=Frame('Frame A'); 【例 15.9】设置 Java 对象的属性 示例代码设置如下: >> setTitle(fa,'New A') >> %获取 fa 的属性 >> title=getTitle(fa) title = New A >> fb.setTitle('New B') >> title=getTitle(fb) title = –523– MATLAB 7.0 New B 本例采用了两种方法设置了不同对象的同一个属性。修改 Java 对象的属性还可以像操作 句柄图形对象一样使用 set 或者 get 函数。而且在 M 语言中创建的 Java 对象可以通过 save 和 load 命令保存到 MAT 数据文件中和加载到当前的工作空间中。 在应用 Java 对象时需要注意 Java 对象在 MATLAB 中属于一类特殊的数据类型,几乎所 有的 MATLAB 数据都可以向 Java 数据对象转变,但是 Java 的对象一般不能直接参与 MATLAB 的数据运算,需要进行相应的类型转换才可以。 【例 15.10】Java 的数据 示例代码设置如下: >> %创建一个双精度的 Java 对象 >> jD=java.lang.Double(33) jD = 33.0 >> jD+3 ??? Function 'plus' is not defined for values of class 'java.lang.Double'. Error in ==> plus at 14 builtin('plus', varargin{:}); >> whos Name Size Bytes Class jD 1x1 java.lang.Double Grand total is 1 elements using 0 bytes >> %不能相加,jD 是一种特殊的数据类型 >> %强制类型转换 >> double(jD) ans = 33 >> ans+3 ans = 36 最后在 MATLAB 中创建 Java 的数据,需要用到一个函数: javaArray(‘package_name,class_name’,x1,…xn) 其中 x1~xn 是数组每一维的尺寸。 15.5.2 应用示例 在 M 语言中应用 Java 类的主要目的之一就是充分利用 Java 语言的网络功能,例如读取 网络上的信息等。 【例 15.11】读取 URL 信息 示例代码设置如下: function readURL(website) –524– 第 15 章 应用程序接口 %read website's data,if no input,read default homepage. if nargin==0 website='http://www.sina.com.cn'; end %create URL object url=java.net.URL(website); %create input stream is=openStream(url); isr=java.io.InputStreamReader(is); br=java.io.BufferedReader(isr); %read network page's data for i=0:44 s=readLine(br); end readLine(br) readLine(br) readLine(br) 本例大部分都是 Java 的语法结构,首先创建了 Java 的 URL 类对象,然后利用 Java 的 I/O 流的功能从互联网的网页上读取了信息,这里将互联网的网页看作纯文本文件读取,最后三 行每一句都读取文本中的一行。运行结果如下: >> readURL ans = A.an02:link {text-decoration:none;color:#1110AC} ans = A.an02:visited {text-decoration:none;color:#65038e} ans = A.an02:active,A.an02:hover {text-decoration:none;color:#ff0000} 15.6 MATLAB 中的 DDE 技术 动态数据交换 DDE(Dynamic Data Exchange)是允许各 Windows 应用程序间交换数据 的通信机制。Windows 平台上的 MATLAB 作为一个应用程序,也具有借助 DDE 与其他应用 程序通信的功能。 15.6.1 关于 DDE 的一般性说明 应用程序可以借助 DDE 通话实现彼此间的通信。请求建立对话的应用程序称为客户 (Client),而响应对话请求的应用程序被称为服务器(Server)。 当客户应用程序创建 DDE 对话时,必须识别被呼叫服务器的两个 DDE 参数:服务名 –525– MATLAB 7.0 (Service name),即被请求对话的应用程序名;话题(Topic),即对话主题。这两个参数的组 合构成了区分不同对话的惟一标识。 每个作为服务器的应用程序都有惟一的服务名。它们通常就是该应用程序的(不带扩展 名)可执行文件名。比如 matlab 是 MATLAB 的服务名。 15.6.2 DDE 中的 MATLAB 服务器 视客户应用程序的具体情况而定,客户可以采用不同方法访问作为服务器的 MATLAB。 假如客户应用程序能够提供管理 DDE 对话的函数或宏,则必须充分利用那些函数和宏;假 如客户应用程序是自编的,则最好利用 MATLAB 引擎库或直接利用 DDE。MATLAB 用作服 务器时的工作原理如图 15-3 所示。此时客户应用程序是通过 DDE 函数与 MATLAB DDE 服 务器模块进行通信的。客户 DDE 函数可以由客户应用程序提供,也可以由 MATLAB 引擎库 提供。 图 15-3 DDE MATLAB 服务器工作模式 DDE 对话内容由一组预习规定的参数名称组成。当 MATLAB 作为 DDE 服务器使用时, 所能选用的具体名称和它们间的层次关系如图 15-4 所示。由图可见,MATLAB 有两类“话 题”System 和 Engine。而每类话题又包含几个不同的内容细节。例如,Engine 话题就其内容 性质而言又分为两类,即客户把指令发给 MATLAB 计算(EngEvalString)和客户向 MATLAB 索取结果;第二类由于数据性质的不同,又分为文字结果(EngStringResult),图形结果 (EngFigureResult),以 Text 格式或 XL 表格式把对话内容约定为索取某个名称的矩阵。 图 15-4 DDE 中 MATLAB 作为服务器使用时定义对话的参数名称层次表 15.6.3 DDE 中的 MATLAB 客户 当 MATLAB 以客户身份建立 DDE 通信时,其工作原理如图 15-5 所示。图中的客户模 块可由一系列的函数构成(见附录),本节将通过一个例子展示各指令间的配合使用。 –526– 第 15 章 应用程序接口 图 15-5 MATLAB 作为客户时的 DDE 原理图 【例 15.12】设计一个 DDE 对话程序。MATLAB 作为客户,Excel 作为服务器,本例演示 DDE 的创建和关闭,热连接的建立和使用。程序(dde_disp.m)如下: clear; h=surf(peaks(20)); %绘制曲面图形并产生图柄 h z=get(h,'zdata'); %得到曲面的 Z 坐标数据 chann=ddeinit('excel','Sheet3'); %为两者的 DDE 对话建立通道 chann range2='r1c1:r20c20'; %为空白表格指定区域,起名为 range2 rc=ddepoke(chann,range2,z); %借助通道 chann,将数据 z 送到指定位置 range2。操作成功,rc 为 1 %...借助通道 chann,在 MATLAB 与指定区域 range2 之间建立热连接… rc=ddeadv(chann,range2,'set(h,"zdata",z);','z'); %Excel 表格 Sheet3 的 range 区域中任何数据的改动,引发如下操作: %立即引起其后指令的执行;MATLAB 空间中的 z 变量被实时更新。 %...切断 MATLAB 与 range2 的热连接… hc=uimenu(gcf,'Label','关闭'); hc1=uimenu(hc,'Label','关热连接','Callback','ddeunadv(chann,range2);'); hc2=uimenu(hc,'Label','关闭对话','Callback','ddeterm(chann);'); hc3=uimenu(hc,'Label','关图形窗','Callback','close;'); 在 Excel 开启的前提下,运行上述程序将产生如图 15-6 和图 15-7 所示的两个界面。 10 5 0 -5 -10 20 15 20 10 5 15 10 5 00 图 15-6 运行后生成的图形 –527– MATLAB 7.0 图 15-7 运行后产生的表格数据 将 Sheet3 表格中的 A1 元素修改为-9,如图 15-8 所示,由于该数据区和所画曲面之间 建立了热连接,所以可以观察到图形发生了如图 15-9 的变化,在曲面正前方处产生了一个向 下的尖角,这就是热连接的作用使得图形曲面随表格数据的变化同步变化。 所建立的热连接和对话通道若不再使用,则应该关闭节省资源。本例在图形窗上为关闭 热连接和对话通道设立了专门的菜单如图 15-10 所示。如果选中其中的“关热连接”选项, 则以后图形将不会随数据表格的变化而变化。 图 15-8 改变 A1 的值图 10 5 0 -5 -10 20 15 20 10 5 15 10 5 00 图 15-9 曲面产生相应的下尖角图 –528– 图 15-10 为关闭热连接和对话通道设计的菜单 第 15 章 应用程序接口 注意:假如建立的通道没有正常关闭,那么同一个程序的再运行可能出现通道识别错误。在这种 情况下,改变通道代号或者冷启动计算机可能克服故障。 15.7 MATLAB 中的 ActiveX 技术 15.7.1 关于 ActiveX 的一般性说明 ActiveX 是一种基于 Microsoft Windows 操作系统的组件集成协议,是各种面向对象技术 的集合。这些技术共有的基础是“组建对象模型(Component Object Model)”,简称 COM。 借助 ActiveX,开发商和终端用户就能把来自不同商家的 ActiveX 组件无缝地集成在自己的 应用程序中,从而完成特定的目的。不仅节省了开发时间,而且有效地避免了低水平的重复 开发。 每个 ActiveX 都支持一个或多个赋名的界面,而界面是一组逻辑相关方法、属性和事件 的组合。方法类似于请求对象实现某动作的函数;属性是对象持有的状态参数;事件是将控 制交回客户(Client)的通知,与图形句柄的 callback 相似。MATLAB 支持两种 ActiveX 技 术,即 ActiveX 控件封装集成和 ActiveX 自动化。ActiveX 控件是指那些可视、能编程的集成 于 ActiveX 容器的应用组件,比如 Microsoft Internet Explorer 和 Web Browser control。 ActiveX 自动化使得 MATLAB 能施控和受控于其他组件。当 MATLAB 受控于其他组件 时,表现为自动化服务器(Automation server),其功能包括:在 MATLAB 空间中执行指令, 与 MATLAB 空间直接交换数据;当 MATLAB 控制其他组件时,表现为自动化客户(Automation client),其功能是 MATLAB 借助 M 文件指示和操纵自动化服务器。 MATLAB 自动化客户的功能仅是 MATLAB ActiveX 控件封装集成功能的子集。所有的 ActiveX 控件都是 ActiveX 自动化服务器,但不是所有的自动化服务器一定是 ActiveX 控件。 那些不是控件的自动化服务器将不可被物理地、可视地镶嵌于客户应用中。MATLAB 本身是 服务器而不是控件,所以不能被镶嵌在其他客户应用中。然而,由于 MATLAB 是控制容器, 所以其他的 ActiveX 控件可以内嵌在 MATLAB 中。 15.7.2 MATLAB 的 ActiveX 自动化 1.MATLAB ActiveX 自动化控制器(客户) 如果要 MATLAB 通过 ActiveX 自动化客户支持调用其他 ActiveX 组件,那么必须先查阅 该 ActiveX 组建的相关文件,从中得到该组件的名字(ProgID)、该组件所采用的接口名、方 法(Method)、属性(Property)和事件(Event)等。 ActiveX 的创建和操作函数见附录。指令 actcontrol 用于创建 ActiveX 自动化客户支持。 该指令运行后将引出指定组件名(比如 Excel、Application)的对象默认界面。通过该对象属 性的获取和设置、方法的激活,就可以改变该对象的界面和行为。 【例 15.13】MATLAB 以自动化客户的资格通过 M 脚本文件把 Microsoft Excel 用作自动 –529– MATLAB 7.0 化服务器。本例演示调用自动化服务器的基本指令。Excel 默认界面的开启;增添工作簿 (Workbook);改变激活的当前页(Worksheet);MATLAB 与 Excel 之间的数据传递;Excel 的数据保存。(图 15-11 给出了第 21 行程序执行前产生的 Excel 界面) 程序(actexcel.m)如下: excel=actxserver('Excel.Application');%启动 Excel 并返回名为 excel 的 Activex 服务器对象 disp('为看清 Excel 界面及其变化,请把 MATLAB 界面调整的远小于屏幕!') disp('按任意键,将可看到“Excel 界面”出现。') pause set(excel,'Visible',1); %使开启的 Excel 默认界面可见 disp('按任意键,可见到 Excel 界面出现第一张表激活的“空白工作簿”。') pause wkbs=excel.Workbooks; %新工作簿句柄 Wbk=invoke(wkbs,'Add'); %产生空白的新工作簿 disp('按任意键,当前激活表由第一张变为指定的第二张。') pause Sh=excel.ActiveWorkBook.Sheets; %当前激活工作簿的表格句柄 sh2=get(Sh,'Item',2); invoke(sh2,'Activate'); %取得第二张表的句柄 %使第二张表为当前激活页 disp('按任意键,把 MATLAB 空间中的 A 矩阵送到 Excel 的指定位置。') pause Actsh=excel.Activesheet; %当前激活表的句柄 A=[1,2;3,4]; actshrng=get(Actsh,'Range','A1','B2'); %得到当前表指定区域的句柄 set(actshrng,'Value',A); %把 A 矩阵送到 Excel 的指定区域 disp('按任意键,获取 Excel 指定区域内的数据,') %第 21 行 disp('并以 MyExcel.xls 文件形式保存在 D:\MATLAB7\work 目录上。') pause rg=get(Actsh,'Range','A1','B2'); B=rg.value; %得到 Excel 指定区域句柄 %获取指定区域上的值 B=reshape([B{:}],size(B)); invoke(Wbk,'SaveAs','D:\MATLAB7\work\MyExcel.xls');%把 Wbk 工作簿保存在指定目录下 disp('按任意键,关闭 excel 句柄代表的 Excel。') pause invoke(excel,'Quit'); %关闭 Excel –530– 第 15 章 应用程序接口 图 15-11 第 21 行程序执行前产生的 Excel 界面 2.MATLAB ActiveX 自动化服务器 通过 MATLAB ActiveX 自动化服务器,用户可以在自己的应用程序中执行 MATLAB 命 令,并可以与 MATLAB 的工作空间交换数据。将 MATLAB 作为服务器使用时,用户必须首 先查阅希望使用自动化服务器的应用程序的文档。查明如何在控制器中开启自动化服务器; MATLAB ActiveX 对象在系统注册表中定义的名字,即 ProgID。通常 ProgID 取以下两个名 字中的一个:MATLAB.Application(将启动的 MATLAB 自动化服务器作为“共享”服务器); MATLAB.Application.Single(将启动的 MATLAB 自动化服务器作为“专用”服务器独享)。 –531– 附录 A.1 常用命令和函数 表1 变量名称 ans pi eps inf NaN 特殊变量名表 变量含义 MATLAB 中默认变量 圆周率 计算机中的最小数,PC 机上为 2−52 无穷大,如 1/0 不定值,如 0/0, ∞ / ∞ ,0* ∞ 变量名称 i(j) nargin nargout realmin realmax 变量含义 复数中的虚数单位 所用函数的输入变量数目 所用函数的输出变量数目 最小可用正实数 最大可用正实数 表2 函数分类 运算符 关系操作符 函数名 + - * .* ^ .^ \ / .\ ./ == ~= < > <= >= 运算符与操作符 说明 加法 减法 矩阵乘法 数组乘法 矩阵乘方 数组乘方 矩阵右除 矩阵左除 数组左除 数组右除 等于 不等于 函数分类 关系操作函数 逻辑运算符 小于 大于 逻辑运算函数 小于等于 大于等于 函数名 eq(A,B) ne(A,B) lt(A,B) gt(A,B) le(A,B) ge(A,B) & | ~ and(A,B) or(A,B) not(A,B) xor(A,B) any(A) all(A) 说明 等于 不等于 小于 大于 小于等于 大于等于 逻辑与 逻辑或 逻辑非 逻辑与 逻辑或 逻辑非 逻辑异或 向量 A 中有非零元素时 返回 1;矩阵 A 某一列有 非 0 元素时此列返回 1 向量 A 中所有元素非 0 时返回 1;矩阵 A 中某一 列所有元素非 0 时,此列 返回 1 表3 函数分类 三角函数 函数名 sin sinh asin asinh cos cosh acos acosh tan tanh atan atanh sec sech asec 基本数学函数 说明 正弦函数 双曲正弦函数 反正弦函数 反双曲正弦函数 余弦函数 双曲余弦函数 反余弦函数 反双曲余弦函数 正切函数 双曲正切函数 反正切函数 反双曲正切函数 正割函数 双曲正割函数 反正割函数 函数分类 三角函数 其他常用 计算函数 函数名 asech cot coth acot acoth fix round floor rem exp log log10 sort abs 附录 说明 反双曲正割函数 余切函数 双曲余切函数 反余切函数 反双曲余切函数 向零方向取整 四舍五入到最近的整数 向无穷大方向取整 求两整数的相除的余数 指数函数 自然对数函数 以 10 为底的对数函数 开方函数 绝对值函数 表4 函数名 isempty(A) isglobal(A) ishold isieee isinf(A) isletter(A) isnan(A) isreal(A) isspace(A) isstr(A)或 ischar(A) isstudent(A) isunix(A) isvms(A) 测试函数 说明 若参量 A 为空,返回 1;否则,返回 0 若参量 A 为全局变量,返回 1;否则,返回 0 当前绘图状态保持是 ON,返回 1;否则,返回 0 计算机执行 IEEE 运算,返回 1;否则,返回 0 若参量 A 为无穷大,返回 1;否则,返回 0 若参量 A 为字母,返回 1;否则,返回 0 若参量 A 为不定值,返回 1;否则,返回 0 若参量 A 无虚部,返回 1;否则,返回 0 若参量 A 为空格字符,返回 1;否则,返回 0 若参量 A 为一个字符串,返回 1;否则,返回 0 若 MATLAB 为学生版,返回 1;否则,返回 0 若计算机为 UNIX 系统,返回 1;否则,返回 0 若计算机为 VMS 系统,返回 1;否则,返回 0 表5 函数分类 常用矩阵函数 函数名 cond condest cross det dot eig eigs inv norm normest rank 矩阵函数 矩阵的条件数值 1-范数矩阵条件数值 矩阵的叉积 矩阵的行列式值 矩阵的点积 矩阵的特征值 矩阵的特征值 矩阵的逆 矩阵的范数值 矩阵的 2-范数值 矩阵的秩 说明 –533– MATLAB 7.0 从入门到精通 函数分类 常用矩阵函数 矩阵分解函数 函数名 orth rcond trace triu tril diag exmp exmp1 exmp2 logm sqrtm cdf2rdf rref rsf2csf rot90 fliplr flipud reshape funm chol eig hess lu null qr qz schur svd svds 说明 矩阵的正交化运算 矩阵的逆条件数值 矩阵的迹 上三角变换 下三角变换 对角变换 矩阵的指数运算 Pade 法进行矩阵的指数运算 Taylor 级数进行矩阵的指数运算 矩阵的对数运算 矩阵的开方运算 复数对角矩阵转换成实数块对角形矩阵 转换成逐行递减的阶梯矩阵 实数块对角形矩阵转换成复数对角矩阵 矩阵逆时针方向旋转 左、右翻转矩阵 上、下翻转矩阵 改变矩阵的维数 一般的矩阵函数 矩阵的 Cholesky 分解 矩阵的特征值分解 矩阵的 Hessenberg 分解 矩阵的 LU 分解 奇异值分解求得的矩阵的零空间的标准正交基 矩阵的 QR 分解 矩阵广义特征值的 QZ 分解 矩阵的 Schur 分解 矩阵的奇异值分解 矩阵的奇异值分解 续表 表6 函数名 sparse spidiags spconvert find speye sprand sprandn sprandsym full nnz nonzeros nzmax spalloc spfun 稀疏矩阵函数 说明 最常用的稀疏矩阵生成函数 以对角带生成稀疏矩阵 由外部数据输入生成稀疏矩阵 求非零元素在稀疏矩阵中的索引 生成稀疏单位矩阵 生成稀疏的均匀分布随机矩阵 生成稀疏的正态分布随机矩阵 生成稀疏的对称随机矩阵 把稀疏矩阵转化为满矩阵 稀疏矩阵非零元素的个数 稀疏矩阵非零元素的值 存放非零元素所需内存 为非零元素分配内存空间 求非零元素的函数值 函数名 spones sprank spy issparse colmmd colperm dmperm randperm symmmd gplot etree etreeplot treeplot 说明 用 1 代替非零元素 稀疏矩阵的秩 显示稀疏矩阵的结构图 判断是否是稀疏矩阵 列最小度 按列排列向量 矩阵的 DuITnageMendelsohn 分解 随机排列向量 最小对称度 按图论画图 给出矩阵的消元树 画消元树图 画结构树图 –534– 表7 [] zeros ones eye rand randn company magic hilb invhilb 矩阵名称 特殊矩阵 说明 空矩阵 零矩阵 1 矩阵 单位矩阵 均匀分布的随机矩阵 正态分布的随机矩阵 伴随矩阵 魔方矩阵 Hibert 矩阵 逆 Hibert 矩阵 表8 conv deconv poly polyder polyfit polyval polyvalm ppval residue roots 函数名 多项式运算函数 多项式乘法 多项式除法 用根生成多项式 多项式导数 多项式拟合 多项式求值 多项式求值 分段多项式求值 部分分式展开 多项式求根 说明 表9 函数分类 通用的数据分析函数 有限差分函数 相关关系函数 相关关系函数 数据分析函数 函数名 max min mean median std sum prod hist trapz quad quad8 cumprod cumsum cumtrapz sort sortows dff gradient del2 corrcoef cov subspace 说明 求最大元素 求最小元素 求元素平均值或列元素的平均值 求列元素的中值 求元素标准差 求各列元素的和 求列元素的积 直方图 梯形法求数值积分 Simpson 法求数值积分 Cotes 法数值积分 求列元素的累计积 求列元素的累计和 梯形法求累计数值积分 按升序对元素进行排序 按升序排列矩阵各行 求差分和近似导数 求近似梯度 求离散 Laplace 算子 求相关系数 求协方差矩阵 求两个子空间之间的夹角 附录 –535– MATLAB 7.0 从入门到精通 表 10 函数名 filter filter2 conv conv2 fft fft2 ifft 傅立叶变换函数 说明 一维离散时间滤波器 二维离散时间滤波器 卷积 二维卷积 快速傅立叶变换 二维快速傅立叶变换 逆快速傅立叶变换 函数名 ifft2 abs angle unwrap fftshift cplxpair nexttpow2 说明 二维逆快速傅立叶变换 模 相角 消除相角突变 平移零点到频率中心 把数字分类为复共轭对 最靠近 2 的次幂 表 11 函数分类 符号表达式运算 符号可变精度运算 符号表达式的化简 符号矩阵的运算 符号矩阵的运算 函数名 sym syms numden symadd symsub symmul symdiv sympow symop compose finverse symsum symvar numeric eval sym poly2sym sym2poly charpoly digits vpa pretty collect horner factor expand simple simplify transpose determ det inv rank eig eigensys svd singvals jordan 符号工具箱函数 建立符号表达式 建立符号表达式 说明 提取分子与分母 符号加法 符号减法 符号乘法 符号除法 符号表达式的幂运算 符号运算 符号表达式的复合函数运算 符号表达式的反函数运算 求表达式的符号和 求符号变量 符号表达式转换为数值表达式 符号表达式转换为数值表达式 数值表达式转换为符号表达式 将等价系数向量转换为它的符号多项式 将符号多项式转换为它的等价系数向量 特征多项式 设置可变精度 可变精度计算 显示方便查看的符号表达式 合并符号表达式的同类项 把一般的符号表达式转换为嵌套形式的符号表达式 对符号表达式进行因式分解 对符号表达式进行展开 对符号表达式进行化简 求解符号表达式的最简形式 符号矩阵的转置 符号矩阵的行列式运算 符号矩阵的行列式运算 符号矩阵求逆运算 符号矩阵求秩运算 求符号矩阵的特征值、特征向量 求符号矩阵的特征值、特征向量 符号矩阵的奇异值运算 符号矩阵的奇异值运算 符号矩阵的约当标准型运算 –536– 函数分类 符号微积分 符号画图 符号方程的求解 函数名 limit diff int taylor ezplot fplot solve linsolve fsolve dsolve 符号极限 符号微分 符号积分 泰勒级数展开 符号函数画图 符号函数画图 代数方程的求解 齐次线性方程组的求解 非线性方程组的求解 微分方程的求解 说明 附录 续表 表 12 函数名 fclose feof ferror fgetl fgets fopen fprintf fread frewind MATLAB 低级文件 I/O 函数 说明 关闭文件 检查是否到文件尾 查询文件 I/O 的出错信息 从文件中读一行,并忽略回车符 从文件中读一行,并包括回车符 打开文件 把格式化数据写到文件中 从文件中读二进制数据 将打开的文件的指针反绕 函数名 fscanf fseek ftell fwrite sprintf sscanf tempname tempdir 说明 从文件中读格式化数据 设置文件指针的位置 获取文件指针的位置 向一个文件中写入二进制数据 把格式化数据写入字符串 从格式化字符串中读入数据 建立临时文件名 读出临时文件名 表 13 函数分类 程序设计标识函数 程序结构控制流 程序结构控制流 程序设计与文件调试函数 函数名 script function eval feval global for end while if elseif else switch case otherwise continue break return echo error try catch 说明 命令式 M 文件 函数式 M 文件 执行字符串 执行字符串定义的函数 定义全局变量 预定的次数重复执行的循环语句 与 for,while,if,switch 语句匹配的结束标志语句 以不定次数执行的循环语句 条件执行语句 与 if 匹配使用 与 if 匹配使用 分支选择语句 与 switch 匹配使用 与 swich 匹配使用 结束本次循环,判断是否执行下一次循环 终止本次循环,跳出最内层的循环 返回调用函数 显示执行的 M 文件的每条命令 出错信息显示 对异常进行处理 与 try 匹配使用 –537– MATLAB 7.0 从入门到精通 函数分类 交互式输入 调试命令 函数名 input keyboard pause dbclear dbcont dbdown dbquit dbstack dbstatus dbstep dbstop dbtype dbup 说明 提示用户输入 通过键盘输入数据 等候用户响应 取消断点 断点后重新运行 工作空间下移 退出调试模式 列表显示堆栈调用 列表显示所有的断点 执行一行或多行 设置断点 列表显示带行号的 M 文件 工作空间上移 续表 表 14 函数名 Isstr Blanks deblank eval isletter strcmp findstr strrep upper lower 说明 判断是否是字符 空格符 删除空格符 执行字符串 确定字符串中的字符是否为字母 字符串比较 在其他字符串中寻找字符串 用一个字符串代替另一个字符串 把字符串中的字符变成大写形式 把字符串中的字符变成小写形式 字符串函数 函数名 abs setstr num2str str2num str2mat hex2num hex2dec dec2hex sprintf sscanf 说明 把字符串变成 ASCII 码值 把 ASCII 码值变成字符串 把数字变成字符串 把字符串变成数字 把字符串变成文本矩阵 把十六进制字符串变成 IEEE 浮点数 把十六进制字符串变成十进制数 把十进制数变成十六进制字符串 把带格式的数字变成字符串 把字符串变成带格式的数字 表 15 函数分类 基本二维 绘图函数 坐标轴控 制函数 硬拷贝和 打印函数 –538– 函数名 plot loglog semilogx semilogy polar plotyy axis zoom grid box hold axes subplot print printopt orient 二维图形函数 说明 二维线形图 双对数坐标 单对数坐标图,x 轴为对数坐标,y 轴为线性坐标 单对数坐标图,y 轴为对数坐标,x 轴为线性坐标 极坐标图 双(y)轴图 控制坐标轴比例和外观 放大或缩小 网格显示控制 坐标轴外框显示 控制是否保持当前图形 在指定位置创建坐标轴 创建子图图区 打印图形或仿真系统,或保存图形到 M 文件 打印机默认设置 设置纸张方向 函数分类 图形标注函数 函数名 Plotedit legend title xlabel ylabel textlabel text gtext 编辑和标注图形工具 图形图例 图形标题 x 轴标签 y 轴标签 文本标签 文本注解 用鼠标放置文本 说明 表 16 函数分类 基本三维图形函数 颜色控制函数 光照控制函数 相机控制函数 透明控制函数 高水平相机控制函数 视点控制函数 三维图形函数 函数名 plot3 mesh surf fill3 colormap caxis shading hidden brighten colordef graymon surf1 lighting material specular diffuse surfnorm campos camtarget camva camup camproj alpha alphamap alim camlight lightangle view viewmtx rotate3d 说明 三维线形图 网格图 表面图 填充的三维多边形 颜色查看表 颜色按坐标轴比例设置 颜色阴影模式 网格隐藏 加亮或变暗色图 颜色默认设置 灰度监视器图形默认设置 给三维阴影表面添加光照 光照模式 材料反射模式 特殊反射 漫反射 表面法向 相机位置 相机目标 相机视角 相机抬升向量 相机投影 透明模式 透明查看表 透明比例 创建和设置光线的位置 光线的极坐标位置 指定三维图的视点 察看转换矩阵 交互旋转三维图 附录 续表 –539– MATLAB 7.0 从入门到精通 表 17 函数分类 特殊二维图形函数 等值线图函数 特殊三维图参数 实体模型函数 函数名 area bar barh comet errorbar ezplot ezpolar feather fill fplot hist pareto pie plotmatrix scatter stem stairs contour contourf contour3 clabel ezcontour ezcontourf bar3 bar3h comet3 ezgraph3 ezmesh ezmeshc ezplot3 ezsurf ezsurfc meshc meshz pie3 ribbon scatter3 stem3 surfc trisurf trimesh waterfall cylinder sphere ellipsoid patch surf2patch 特殊图形函数 说明 面积图 二维条形图 二维水平条形图 彗星图 误差条图 简易函数绘图器 简易极坐标绘图器 羽列图 填充的二维多边形 函数图形 直方图 帕雷托图 饼图 散点图矩阵 散点图 火柴杆图 阶梯图 等值线图 填充的等值线图 三维等值线图 给等值线图添加标签 简易等值线图生成器 简易填充等值线图生成器 三维条形图 三维水平条形图 三维彗星图 简易一般表面绘图器 简易三维网格绘图器 简易三维网格图叠加等值线图生成器 简易三维参数曲线绘图器 简易曲面绘图器 简易三维曲面叠加等值线图绘图器 网格图叠加等值线图 窗帘图 三维饼图 带形图 三维散点图 三维火柴杆图 曲面图叠加等值线图 三角形表面图 三角形网格图 瀑布图 创建柱体 创建球体 创建椭球体 创建阴影 将表面数据转换为阴影数据 –540– 函数分类 动画和快照函数 体积和向量 可视化函数 图形显示和文件 I/O 函数 与颜色相关的函数 函数名 capture moviein getframe movie rotate frame2im im2frame vissuite isosurface isonormals isocaps isocolors contourslice slice streamline stream3 stream2 quiver3 quiver divergence curl coneplot streamtube streamribbon streamslice streamparticles interpstreamspeed reducevolume volumebounds smooths reducepatch shrinkfaces image imagesc colormap gray contrast brighten colorbar imread imwrite imfinfo spinmap rgbplot colstyle ind2rgb 说明 抓取当前图形 初始化动画框内存 获取动画框 演示记录的动画框 旋转指定了原点和方向的对象 将动画框转换为索引图片 将索引图片转换为动画框 可视组合 等表面提取器 等表面法向 等表面终端帽盖 等表面和阴影颜色 切片面板中的等值线 体积切片图 二维、三维向量数据的流线图 三维流线图 二维流线图 三维矢量图 矢量图 向量场的差异 向量场的卷曲和角速率 三维流锥图 三维流管图 流带图 切片面板中叠加流线图 流沙图 内插源于速度的流线顶点 减少体积数据 为体积数据返回 x,y,z 和颜色限制 平滑三维数据 减小阴影表面的个数 减少阴影表面的大小 显示图片 数据比例化并图形显示 颜色查看表 线性灰度色图 灰度色图,用于改进图片的对比度 加亮或变暗色图 显示色图 从图形文件中读取图片 从图形文件中写图片 图形文件的信息 旋转色图 绘色图 用字符串说明颜色和风格 将索引图片转换为 RGB 图片 附录 续表 –541– MATLAB 7.0 从入门到精通 表 18 figure gcf clf shg close refresh openfig 函数名 表 19 interp1 interp2 interp3 interft intern spline polyfit 函数名 表 20 ode23 ode23p ode45 fmin fmins fzero 函数名 表 21 set get reset delete gcf gca gco gcbo gcbf findobj drawnow copyobj isappdata getappdata setappdata rmappdata figure axes 函数名 –542– 图形窗口的创建和控制函数 说明 创建图形窗口 获取当前图形的图柄 清除当前图形 显示图形窗口 关闭图形 刷新图形 打开已保存图形的新拷贝或显示已存在的拷贝 插值与拟合 说明 一维线性插值 二维线性插值 三维线性插值 快速 Fourier 变换得到一维插值 多数线性插值 三次样条插值 最小二乘法拟合 非线性数值解法 说明 二三阶 Runge-Kutta 求解常微分方程 二三阶 Runge-Kutta 求解常微分方程并画出结果图 四五阶 Runge-Kutta 求解常微分方程 求一元函数的极小值 求多元函数的极小值 求一元函数的零点值 GUI 图形函数 说明 设置对象属性 获取对象属性 把对象属性重设为默认值 删除对象 获取当前图形对象的句柄 获取当前图形窗口内当前坐标轴的句柄 获得当前图形窗口内当前对象的句柄 获取当前回调对象的句柄 获取当前回调图形的句柄 获取指定的属性值的对象的句柄 绘图 图形对象拷贝 核对应用程序定义的数据是否存在 获取应用程序定义的数据的值 设置应用程序定义的数据的值 删除应用程序定义的数据 创建图形对象 创建坐标轴对象 line rectangle light text patch surface image uimenu uicontextmenu uicontrol dialog uigetfile uiputfile uisetcolor uisetfont pagesetupdlg pagedlg printpreview printdlg helpdlg errordlg msgbox questdlg warndlg inputdlg listdlg 函数名 创建线条对象 创建矩形 创建光线 创建文本对象 创建补片对象 创建曲面对象 创建图像对象 创建下拉式菜单对象 创建内容式菜单对象 创建控件对象 创建对话框 创建文件打开对话框 创建文件保存对话框 创建颜色设置对话框 创建字体设置对话框 创建打印页面设置对话框 创建打印页面设置对话框 创建打印预览对话框 创建打印对话框 创建帮助对话框 创建出错信息提示对话框 创建信息提示对话框 创建问题显示对话框 创建警告信息显示对话框 创建变量输入对话框 创建列表选择对话框 说明 A.2 SIMULINK 的库模块 A.2.1 库模块 Demos library Simulink 演示子库 SIMULINK 基本库 A.2.2 连续模块子库 Continuous Continuous Derivative Integrator Memory State-Space Transfer Fcn 连续模块子库 求导数模块 连续函数积分 记忆模块 状态方程模块 传递函数模块 附录 续表 –543– MATLAB 7.0 从入门到精通 A.2.3 离散模块子库 Discrete Discrete Discrete Filter Discrete-Time Integrator Discrete Transfer Fcn Discrete Zero-Pole Unit Delay Zero-Order Hold 离散模块子库 离散滤波器模块 离散时间积分模块 离散传递函数模块 离散零极点增益模块 单位延迟模块 零阶保持模块 A.2.4 解析函数和查表函数模块子库 Functions&Tables Fcn Functions&Tables MATLAB Fcn Look-Up Table Look-Up Table(2-D) C 语言格式的任意函数模块 解析函数和查表函数模块子库 MATLAB 语言格式的任意函数 一维查表函数模块 二维查表函数模块 A.2.5 一般数学函数子库 Math Abs Combinatorial Logic Gain Logical MinMax Math Mux Product Relational Sign Slider Sum 取绝对值模块 组合逻辑模块 增益模块 逻辑运算模块 取极大值或极小值的模块 一般数学函数子库 复用模块 乘法器 关系运算模块 符号运算模块 滑键增益模块 求和模块 A.2.6 非线性模块子库 Nonlinear Dead Zone Nonlinear Relay Saturation 死区非线性模块 非线性模块子库 继电器模块子库 饱和非线性模块 –544– A.2.7 信号和系统模块子库 Signal&Systems Demux Enable Ground In1 Merge Out1 Signal&Systems SubSystem Trigger Terminator 分用模块 使能模块 接地模块 输入端口模块 汇合模块 输出端口模块 信号与系统模块子库 子系统模块 触发模块 终端模块 A.2.8 信宿模块子库 Sinks Display Scope Sinks Stop To File To Workspace XY Graph 数值显示模块 示波模块 信宿模块子库 终止仿真 把数据存为文件 把数据写为矩阵变量 显示 X-Y 图形 A.2.9 信源模块子库 Sources Clock Constant From File From Workspace Pulse Signal Generator Sine Wave Sources Step 连续仿真时钟模块 恒值输出模块 从文件读数据 从内存读数据 脉冲发生器 信号发生器 正弦波输出 信源模块子库 阶跃输出 附录 –545– MATLAB 7.0 从入门到精通 A.3 应用程序接口函数库 A.3.1 外部程序接口函数库 mexCallMatlab mexErrMsgTxt mexEvalString mexFunction mexGetVariable mexPutVariable mexWarnMsgTxt mxCalloc mxSetClassName mxCreateDoubleMatrix mxCreateNumericMatrix mxCreateString mxDestroyArray mxGetDimensions mxGetM mxGetN mxGetNumberOfDimensions mxGetNumberOfElements mxGetPi mxGetPr mxGetString mxIsChar mxIsComplex mxIsDouble mxIsFromGlobalWS mxIsNumericMatrix mxSetM mxSetN mxSetName 调用 MATLAB 内建函数、M 文件等 发布出错信息并中断 MEX 执行 利用 MATLAB 计算送去指令 C 语言 MEX 文件的固定格式的接口函数 将其他空间内的变量送入 MEX 空间 将 MEX 空间内的变量送入其他空间 仅发布出错信息 配置动态内存 为变量命名 创建二维未赋值双精度浮点数组 创建数值矩阵 创建字符串 释放动态分配的内存 获取指定(高维)数组内各维的大小 获取指定数组的行数 获取指定数组的列数 获取指定数组的维数 获取数组所含元素总数 获取指定数组的虚数部分的数据指针 获取指定数组的实数部分的数据指针 获得字符串 判断指定数组是否字符串类型 判断指定数组是否为复数型 判断指定数组是否为双精度类型 判断数组来自 MATLAB 全局空间与否 判断指定数组是否为数值类型 设置数组的行数 设置数组的列数 设置数组名 A.3.2 MAT 文件库函数 matClose matDeleteArray matPutVariable matGetVariable –546– 关闭 MAT 文件 从 MAT 文件中删除一个数组 向 MAT 文件中写入 mxArray 变量 从 MAT 文件中读取 mxArray 变量 matOpen matPutArrayb matPutVariableAsGlobal 开启 MAT 文件 将数组写入到 MAT 文件中 将数组以全局变量形式写入 MAT 文件 A.3.3 MATLAB 引擎函数库 engClose engEvalString engGetVariable engOpen engPutVariable engOutputBuffer MessageBox 关闭 MATLAB 引擎 调用引擎计算字符串表达式 从 MATLAB 引擎空间中获取变量 启动 MATLAB 引擎 将 mxArray 变量送入 MATLAB 引擎空间 产生保存 MATLAB 输出的缓冲区 在根屏幕上建立对话框 A.3.4 ActiveX 对象的构造和操作命令 actxcontrol actxserver delete get invoke move propedit release send set 建立一个 ActiveX 控件对象 建立一个 ActiveX 自动化服务器对象 删除一个 ActiveX 对象 从接口获取属性值 激活窗口 在父窗上移动对象或改变其大小 要求控件显示其内建的属性页 释放 ActiveX 对象 显示事件列表 对接口属性进行设置 A.3.5 动态数据交换函数 ddeinit ddeexec ddepoke ddereq ddeterm ddeadv ddeunadv Engine EngEvalString EngFigureResult EngStringResult 创建 DDE 对话通道 向服务器发送需执行的字符串 向服务器发送数据 从服务器得到数据 关闭 DDE 对话通道 建立热连接 释放热连接 MATLAB 作 DDE 服务器的引擎工作模式 向 MATLAB 发送需执行的字符串 从 MATLAB 获取图形结果 从 MATLAB 获取非图形结果 附录 –547–

    Top_arrow
    回到顶部
    EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。