☰
Matlab三角函数实用指南:从弧度制到向量化的核心技巧
2026/10/7 22:43:39 网站建设 项目流程

1. 三角函数在Matlab中的核心用武之地

1.1 为什么三角函数是Matlab的常客

Matlab里三角函数这个家族,说大不大,说小也真不小。你要是去查文档,会发现sin、cos、tan、asin、acos、atan、sinh、cosh、atan2,再加上它们的“带d版本”sind、cosd、tand、atand……一眼望过去至少有二十来个。很多刚接触Matlab的同学会觉得,不就是高中学过的sin和cos嘛,键盘敲一下就出来了,还能有什么花样。但实际用下来你会发现,三角函数的坑远比你想象的多:角度制还是弧度制?输入的是数组怎么办?结果里的微小误差要不要处理?反三角函数的值域为什么和手算对不上?

这篇速成笔记就是为了把这些事情一次讲透。我会从最基本的sin、cos开始,一路讲到双曲函数、复数域上的三角函数、矩阵批量计算、绘图实战,最后再把我自己踩过的一些坑整理成排查清单。无论你是刚安装好Matlab准备做信号处理课设的新手,还是已经写了不少脚本但从来没系统性整理过三角函数用法的老手,这篇笔记都可以随手翻一翻。

1.2 速成之前先分清角度制与弧度制

很多人第一次在Matlab里算三角函数就翻车,根子在于角度制和弧度制没分清。Matlab的默认三角函数sin、cos、tan,输入输出都是弧度制。也就是说,sin(30)算的不是“30度”的正弦,而是“30弧度”的正弦,结果约为-0.988。如果你拿计算器按sin30得到0.5,然后来Matlab里一验算发现对不上,那不是Matlab坏了,是单位搞混了。

这里我建议大家把弧度制当成Matlab的“母语”——毕竟sin、cos、tan这类核心函数在数学定义上就是以弧度为标准的。而如果你习惯用角度思考,比如算一个30度角的对边长度,那么有两种做法:第一种是自己手动转弧度,sin(30 * pi / 180);第二种是直接用Matlab专门提供的角度版本sind(30),结果就是0.5。同理还有cosd、tand、atand、atan2d,这些带d的函数都是把单位固定在“度”上,省去了每次乘以pi/180的麻烦。

提示:sind这类角度版本函数,在做角度制几何计算时非常好用,但要注意它们和弧度制版本在输入为数组时行为完全一致,不用担心批量计算时单位处理不一致的问题。

2. 基础三角函数语法速查与细节拆解

2.1 sin、cos、tan与它们的“带d版本”

先从最核心的sin说起。它的基本调用方式就是y = sin(x),其中x可以是标量、向量、矩阵,甚至是多维数组。这一点是Matlab最舒服的地方——你不需要写循环去逐个元素求正弦,直接整个数组丢进去,输出就是同样大小的数组。

x = 0:0.1:pi; % 生成从0到pi、步长0.1的向量 y = sin(x); % 对向量每个元素求正弦

这里的x有32个元素,y也是32个元素,一一对应。这种“整个数组一起算”的风格叫作向量化,是Matlab性能优化的核心原则之一。我在后面的章节会专门展开讲为什么向量化比for循环快,以及它在三角函数场景下具体怎么写。

cos和tan的用法完全一样,没什么好特别说的。真正值得留意的是tand这个函数的一个细节:当输入角度是90、270这类正切函数无定义的点时,tan(pi/2)会因为浮点数表示pi/2不够精确,算出一个非常大的数而不是Inf;但tand(90)则会直接返回Inf,因为它在内部做了特殊处理。这里我建议你在做涉及正切的数值计算时,优先考虑用tand配合角度制,至少能避免在无定义点附近出现莫名其妙的巨大数值。

另外一个容易被忽视的点是sec、csc、cot这些倒数三角函数。Matlab里也有sec、csc、cot以及它们的角度版本secd、cscd、cotd,但实际工程中用得不多,因为直接用1 ./ sin(x)也能得到相同结果。唯一的差别在于,csc这类函数在输入接近0时返回的是Inf还是很大的数——Matlab文档里说csc会把0映射为Inf,而1/sin(x)在x为0时则是Inf(因为Matlab里除零不报错而是给Inf)。两个行为基本一致,但为了代码可读性,我更推荐直接用倒数写法。

2.2 反三角函数的用法与取值范围陷阱

反三角函数是另一个重灾区。asin、acos、atan分别对应反正弦、反余弦、反正切,它们返回的是弧度制角度。这里有个非常关键的取值范围问题:

  • asin返回值的范围是[-pi/2, pi/2];
  • acos返回值的范围是[0, pi];
  • atan返回值的范围是[-pi/2, pi/2]。

这意味着什么呢?举一个非常典型的例子:如果我在平面直角坐标系里有一个点(x, y),想求它相对原点的极角(也就是从x轴正方向逆时针转到该点的角度),手算时我们会觉得这个角度应该能覆盖-pi到pi的整个范围。但如果直接用atan(y/x),你会发现当点在第二、三象限时,结果会跑到[-pi/2, pi/2]区间内,和实际角度差了pi。

这就是atan2存在的意义。Matlab里的atan2(y, x)专门用来解决这个问题:它能够根据x和y的正负号,自动确定角度所在的象限,返回从-pi到pi的完整极角。我在实际写代码时的经验法则是:凡是用atan(y/x)的地方,除非你非常确定x恒大于0,否则一律改成atan2(y, x)。这个习惯省了我无数次肉眼排查角度不对的苦工。

反三角函数的另一个坑是定义域。asin和acos的输入必须落在[-1, 1]区间内,一旦超出这个范围,结果就是复数。比如asin(1.5)在Matlab里不会报错,而是返回一个复数结果1.5708 - 0.9624i。很多人在处理实验数据时没注意到个别数据点超出了[-1,1],结果一整个计算结果变成了复数,后面所有绘图、比较逻辑全乱了。这个问题的排查方法我会在第五章详细讲。

2.3 双曲函数与复数域扩展

说完基本三角函数和反三角函数,我再说说双曲函数。sinh、cosh、tanh这三个函数看起来名字和三角函数很像,但它们是建立在指数函数基础上的:sinh(x)等于(e^x - e^(-x))/2,cosh(x)等于(e^x + e^(-x))/2。它们的图像、性质都和普通三角函数完全不同,但Matlab里的调用语法是完全一致的。

双曲函数在工程上最常出现在两个地方:一个是悬链线的计算——就是两端固定、自然下垂的绳索那条曲线,它的形状正好是cosh函数;另一个是深度学习中常用的tanh激活函数,它的取值范围在[-1, 1]之间,梯度特性比sigmoid好,所以很多神经网络代码里都离不开tanh。如果你在做这几类工作,直接y = tanh(z)就完事了,不需要自己写指数运算。

复数域上的三角函数也是Matlab的一个隐藏优势。大家都知道欧拉公式e^(ix) = cos(x) + i*sin(x),在Matlab里输入exp(i*pi)会得到-1 + 0i,这在验证公式时很直观。而当你把sin的输入从实数换成复数时,Matlab也会自动在复数域上计算。比如sin(1i)的结果是0 + 1.1752i,这个值等于(e - 1/e)/2,也就是sinh(1)。这里面的数学很有意思,但站在实用角度,你只需要知道:如果哪天计算结果莫名其妙出现了虚部,先看看输入数据里是不是有复数“混入”了。

3. 矩阵运算与批量计算的正确姿势

3.1 对整个数组“一键”求三角函数

Matlab里对矩阵求三角函数,语法和对标量求三角函数一模一样。比如我有一个3x3的矩阵A,想求每个元素的余弦:

A = [0, pi/4, pi/2; pi/3, pi/6, 1; 2, 3, 4]; B = cos(A);

这个B就是和A同样大小的3x3矩阵,每个位置都是对应元素的余弦值。这里要注意的是,所谓“对矩阵求三角函数”,其实是逐元素运算,和线性代数里的矩阵乘法、矩阵指数完全不是一回事。后者需要写成A * A或者expm(A),而三角函数家族在这里全部是“点运算”,直接调用即可。

多维数组也一样没问题。rand(2,3,4)生成的三维数组也可以直接传给sin,输出还是同样尺寸的三维数组。我自己在图像处理中处理像素矩阵时,经常会直接把一个二维灰度矩阵丢进sin或者cos里做变换,完全不需要写循环,这对于代码简洁性和可读性都是巨大的提升。

3.2 性能优化:向量化的原则与实用技巧

Matlab圈子里流传着一句话:“让代码跑得快,不是写更好的循环,而是不写循环。”这在三角函数运算里体现得淋漓尽致。假设我想算100万个点的正弦值,最粗暴的写法是:

n = 1000000; x = linspace(0, 2*pi, n); y = zeros(1, n); for k = 1:n y(k) = sin(x(k)); end

这个循环在Matlab里跑起来奇慢无比,因为Matlab是解释型语言,每执行一行都有解释、检查的开销,循环语句尤其吃亏。而如果你直接写:

y = sin(x);

它内部会调用高度优化的底层库,对整套数组进行批量计算,速度快了不止一个数量级。在我自己的测试里,100万个点的正弦计算,循环版本需要大约0.5秒,向量化版本只需要0.02秒左右,差距是25倍。随着数据量增大,这个差距还会进一步拉大。

所以在写任何涉及三角函数的代码时,我的建议只有一条:能向量化就向量化,永远不要主动写for循环去逐点求三角函数。矩阵运算、多维数组、线性空间生成(linspace、meshgrid)这些工具配合起来,几乎所有常见的三角函数计算场景都能用一条语句解决。

4. 实操案例:从波形绘制到信号分析

4.1 案例一:用Matlab画出正弦波叠加效果

纸上谈兵够多了,我们来写几个能直接跑的实战案例。第一个案例是做信号处理的人几乎每天都要干的事情——生成并绘制正弦波。

fs = 1000; % 采样率 1000Hz t = 0:1/fs:1; % 1秒时长 f1 = 5; % 第一个频率 5Hz f2 = 50; % 第二个频率 50Hz y = sin(2*pi*f1*t) + 0.5*sin(2*pi*f2*t); % 叠加信号 plot(t, y); xlabel('时间 (秒)'); ylabel('幅度'); title('5Hz与50Hz正弦波叠加');

这里有一个细节值得展开说一下。sin(2*pi*f*t)是信号处理中最标准的正弦波生成公式,为什么是2pi×频率×时间?因为sin的默认输入是弧度,一个完整的周期是2pi弧度,而f*t表示的是“振荡了几个周期”。比如5Hz的波,在1秒内振荡5个周期,总弧度就是5×2*pi。这个公式如果你理解了,以后生成任何频率的正弦波都不需要查资料,直接推出来就行。

我还想强调采样率的概念。fs = 1000表示每秒钟采样1000个点,也就是相邻两个点的时间间隔是1/1000秒。如果你把采样率设得很低,比如fs = 20,再去画50Hz的正弦波,就会看到波形严重失真——这是“采样定理”的经典体现:采样率必须大于信号最高频率的两倍,否则高频信号会“伪装”成低频信号,这种现象叫混叠。我自己在做教学演示时经常用这个例子让学生亲手体会混叠效应,比任何说教都直观。

4.2 案例二:傅里叶级数视角下的方波拟合

三角函数不仅是用来生成正弦波的,它还能拼出矩形波、锯齿波、三角波这些非正弦波形。这个技巧在电力电子、音频合成、信号分析里都有实际应用。这里我用一个方波拟合的例子来说明。

方波可以看作无穷多个奇次谐波正弦波的叠加:一个频率为f、幅度为1的方波,其傅里叶级数展开为(4/pi) * sum(sin(2*pi*(2k-1)*f*t) / (2k-1), k=1,2,...)。在Matlab里,我们可以用有限的谐波数来逼近方波:

t = 0:0.001:1; f = 1; % 1Hz方波 y = zeros(size(t)); N = 50; % 用50次谐波去逼近 for k = 1:N n = 2*k-1; % 只取奇次谐波 y = y + (4/pi) * sin(2*pi*n*f*t) / n; end plot(t, y);

注意这里我用了for循环,而且循环次数只有50次——这是合理的用法,因为每个谐波需要对整条时间轴计算一次正弦值,而谐波次数本身不可能向量化得那么彻底。真正的优化方式是先用矩阵广播做类似n = 1:2:99的行向量配合t的列向量生成二维正弦矩阵,再按轴求和。但对于50次谐波这种规模,for循环的耗时可以忽略不计,代码可读性反而更好。

这个例子还引出一个非常有用的观察:你在拟合结果中会看到方波的边缘有细微的振荡,这个现象叫“吉布斯现象”。它不是因为代码写错了,而是傅里叶级数的固有特性——有限项近似在间断点附近总会过冲约9%。很多初学者第一次看到这个抖动以为是自己程序的问题,到处排查半天最后发现是数学本质,提前了解就能省下这个冤枉时间。

4.3 符号计算中的三角函数化简

Matlab里还有一个被严重低估的三角函数应用场景——符号计算。如果你安装了Symbolic Math Toolbox,可以用syms声明符号变量,然后让Matlab帮你做三角恒等变换。比如:

syms x expr = cos(x)^2 + sin(x)^2; simplify(expr) % 输出 1

这个功能在做理论推导、化简表达式时非常实用。我记得以前做控制理论作业时,有个系统的传递函数里包含了一堆sin(omega*t)和cos(omega*t)的组合,要手工展开并合并成单个正弦/余弦形式相当繁琐。后来我用combine(expr, 'sincos')这个命令,一步就把它化简成了A*sin(omega*t + phi)的标准形式,连幅度和相位都直接给出表达式,极大减轻了作业负担。

符号计算中常用的还有rewrite,可以把sin/cos改写为tan的半角形式,或者用exp指数形式表示。expand则会把sin(x+y)拆成sin(x)*cos(y) + cos(x)*sin(y)。这些函数本质上做了我们高中背过的那些公式,但计算机做得快、不会错,你只需要告诉它你要展开、化简还是合并就行。

5. 高频踩坑与问题排查实录

5.1 浮点误差导致的“不等于零”

我在2.1节末尾提到的浮点误差问题,在这里展开细说。相信不少人都遇到过这样的困惑:明明sin(pi)就是0,为什么Matlab算出来的结果是1.2246e-16?

这个问题的根源在于,计算机内部用二进制分数来存储数值,而pi是一个无限不循环小数,二进制下同样无法精确表示。Matlab实际参与计算的是一个非常接近pi但并非精确等于pi的数,这个数的正弦自然也不会是精确的0,而是某个极小的值。类似地,cos(pi/2)在Matlab里约等于6.1232e-17而不是0。

这个误差本身很小,通常不影响实际工程。但如果你拿它做判断,比如写if sin(pi) == 0,那就会踩雷。正确的做法是设置一个容差,比如:

result = sin(pi); if abs(result) < 1e-10 disp('近似等于0'); end

或者使用round(result, 10)四舍五入到指定小数位数。在我自己的经验里,判断浮点数是否相等都用abs(a - b) < tol的写法,tol取1e-10左右,既不会误判,也不会因为容差太大掩盖真实差异。这也是数值计算的通用原则:永远不要用==直接比较浮点数,除非你能确保双方都是由整数运算精确产生的。

5.2 输入复数时结果为何“不对劲”

另外一个非常隐蔽的问题出现在输入数据里“混入”了复数时。Matlab有个特性:只要数组里有一个元素是复数,整个数组的运算结果通常就会带上虚部。比如你的x向量里某个值是2 + 1i,那sin(x)的结果就会包含复数。如果你没注意到,后续取实部、绘图、找最大值等操作得到的结果可能都不符合预期。

这个问题的典型场景是反三角函数。前面提到过asin和acos要求输入在[-1, 1]内,一旦某个数据点越界,结果就变成复数,而常系数数据里哪怕只有一个是1.0000001,都会导致输出复数化。我处理实验数据时的经验是,在调用反三角函数之前先检查数据范围:

data = randn(1, 100) * 1.2; % 模拟可能有越界的数据 if any(abs(data) > 1) warning('存在超出[-1,1]的数据点,结果可能包含复数'); end

先预警,再决定是裁剪数据还是剔除异常点,这样才能保证后续计算结果的纯洁性。这个习惯在信号处理、姿态解算等大量使用反三角函数的场景里尤其重要。

5.3 反三角函数边界值与NaN的处理

还有一些边界值问题。比如atan2(0, 0)在Matlab里返回0,这在数学上是有争议的(零向量没有定义方向),但Matlab选择了一个不报错的默认值。如果你在做极坐标转换、航向角计算这类工作,输入数据里确实可能出现x和y同时为0的点,这时返回值0可能会被当作有效数据混入后续统计,产生偏差。我这里建议的方法是在调用atan2之前先筛选掉x == 0 & y == 0的点,或者之后单独处理。

另一个常见情况是三角函数输入为Inf或NaN时。sin(Inf)在Matlab里返回NaN,因为正弦函数在无穷远处振荡、没有确定的极限——这个数学上是合理的。但问题是,如果输入数据采集过程中出现了掉线、漏采,填充成了Inf,那整条计算链路都会变成NaN。这在长时段数据处理的代码里很致命,因为你往往到最后画图时才发现曲线缺了一大段,但已经很难追溯是哪个环节引入的Inf了。我的建议是在数据进入计算管线之前就做一轮清洗:data(~isfinite(data)) = 0;或者用fillmissing插值补全,把Inf和NaN提前处理干净。

5.4 角度制与弧度制混用的“薛定谔错误”

最后再说一个不是错误但在团队协作中极其容易引发灾难的问题:角度制与弧度制混用。我看到过不止一次,一个人写代码时用tand(60)算角度,另一个人后续接手用cos(pi/6)做验证,两边结果差了一个数量级,排查一整天最后发现是某个* pi / 180被多写或少写了一处。

我的个人习惯是:在脚本开头用注释明确标注所有角度相关变量是用角度还是弧度,并且命名时直接带上单位后缀。比如:

theta_deg = 45; % 角度制 theta_rad = deg2rad(theta_deg); % 转换 x = cos(theta_rad); % 后续统一用弧度

命名带上_deg和_rad后缀,成本极低但效果立竿见影。如果你做的是交互式脚本,甚至可以在读取用户输入时显式询问“请输入角度(度):”,然后立刻统一为弧度。这种防御性编程的习惯,能帮你省下无数个加班排查单位Bug的夜晚。

6. 速成笔记的几点实操心得

在我自己过去的项目里,三角函数很少单独作为主角出现,但它永远是配角中的高频选择:波形生成、图像旋转、机器人运动学解算、天线方向图计算、三相电的相位变换……这些领域无一处不用到sin和cos。正因为它是基础中的基础,才更值得花半小时把细节摸透。

最后分享一个我常用的灵感来源:Matlab自带的doc sin文档里,每一页底部都有“See Also”区域,这其实是一座宝藏。我做信号处理项目时,偶然点进atan2的See Also,顺藤摸瓜发现了cart2pol和pol2cart两个坐标转换函数,后来在极坐标绘图和雷达数据处理中帮了大忙。学习Matlab不一定要啃整本官方文档,但多点点See Also、多看看示例代码,往往能发现比自己现造轮子更优雅的解法。

这个系列的速成笔记,我尽量保证每篇都能让读者“打开就能用、用后能记住”。三角函数这篇算是整个系列里数学味道较浓的一篇,但写代码的实用性和高中知识其实是两码事——哪怕你已经把三角恒等式忘光了,只要理解了弧度制与角度制的区别、记住反三角函数的值域限制,再养成向量化和防御性编程的习惯,Matlab里绝大多数三角函数场景都不会再拦住你了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询