资讯动态

用Python绘制经典极坐标曲线:从参数方程到Matplotlib实践

发布时间:2026/10/14 19:47:25 来源:尧图企业网站定制
我最早接触python画曲线是在一个深夜赶项目报告的时候。当时需要往文档里插几张漂亮的数学曲线图手头只有matplotlib结果发现网上很多教程要么只给代码不给原理要么画出来的图歪歪扭扭参数调得一团糟。后来我花了大半天时间把蜗型线、心形线、三叶曲线、四叶曲线、螺旋线这几种经典曲线全部捋了一遍写了一个干净的绘图脚本今天就把它拆开来讲清楚。这篇文章适合两类人一类是刚学python不久想用matplotlib练手做些有趣的数学可视化另一类是已经在用python做数据处理但偶尔需要画几张带极坐标的曲线图却被默认直角坐标系折腾得够呛。我会从极坐标和参数方程的原理讲起把每种曲线的数学表达式、python绘图代码、以及我在实际调试中踩过的坑全部铺开说保证你照着敲一遍就能跑出好看的图。1. 为什么要用参数方程而不是直接画函数很多初学者拿到这类曲线题目的第一反应是用matplotlib画yf(x)不就行了比如画抛物线、正弦波确实是直接给x算y就行。但蜗型线、心形线、三叶曲线这类图它们的本质是在极坐标下定义的直角坐标表达式往往又复杂又绕硬用yf(x)去表述会让你怀疑人生。1.1 极坐标和直角坐标的关系在极坐标里平面上的一个点用(r, θ)表示r是点到原点的距离θ是从极轴通常取x轴正方向逆时针旋转的角度。它和直角坐标(x, y)的换算关系大家高中都学过x r * cos(θ) y r * sin(θ)所以当你有一个极坐标方程r f(θ)时只要让θ从某个区间扫描过去算出每个θ对应的r再转成x和y就能在直角坐标系里画出这条曲线。这个过程就是参数方程绘图的核心思路把θ当作参数x和y都跟着θ走。1.2 为什么曲线题目几乎都离不开θ的密集采样这里有一个关键点计算机画曲线不是“凭空连一条线”而是先在曲线上取足够多的离散点再用线段把这些点连接起来。θ取点越密集折线就越接近光滑曲线。如果θ只取几十个点你看到的就是一个多边形取几千个点肉眼基本分辨不出线段和真实曲线的差别。我自己写这类脚本时习惯把θ设成np.linspace(0, 2*np.pi, 1000)1000个点对于大多数教科书画图场景已经完全够用跑起来也快。如果你要放到论文插图里追求极致光滑可以加到2000甚至5000但要注意线条太多会让导出PDF文件体积变大这个后面会细说。1.3 数形结合先看懂公式再动手写代码画图之前我强烈建议先看一眼r f(θ)的表达式在心里模拟一下θ变大时r会怎么变。比如心形线r a(1 - cosθ)θ0时r0θπ时r2a说明最远处在左边三叶曲线r a*cos(3θ)cos函数正负交替所以花瓣会分好几瓣。这种“先猜图像长什么样”的习惯能让你在代码出错时快速判断是数学公式抄错了还是程序写错了。2. 公共底座极坐标绘图的几个容易忽略的细节画这五种曲线我建议统一用matplotlib的极坐标子图而不是直角坐标加手动画圆。极坐标子图的坐标轴本身就是r和θ的标尺画出来的图自带极坐标系既方便观察曲线形状也省去自己标注角度的麻烦。2.1 一行代码创建极坐标画布matplotlib里创建极坐标子图非常直接import numpy as np import matplotlib.pyplot as plt fig plt.figure(figsize(6, 6)) ax fig.add_subplot(111, projectionpolar)这里projectionpolar是把子图设为极坐标投影的关键参数。如果你忘了写这一行后面不管你画什么得到的都是直角坐标图曲线形态会被完全扭曲。我曾经有一回忘了加polar心形线画出来像个被压扁的苹果排查了半天才发现是投影没设对。2.2 角度范围与方向matplotlib极坐标的默认习惯极坐标子图的默认角度从0开始逆时针为正方向也就是数学里的标准约定。0度在水平向右的位置90度在正上方180度在正左方270度在正下方。对于大部分曲线θ取0到2π就能闭环。但有些曲线比如特定的螺旋线段你可能只画一段弧线就够。控制角度范围的方法很简单ax.set_thetamin(0) ax.set_thetamax(360)注意这里单位是“度”不是弧度。如果你直接用np.linspace(0, 2*np.pi)生成θ然后发现坐标轴标注是0到360别慌——matplotlib内部已经帮你转好了只是显示上习惯用角度。如果你觉得这个转换绕也可以在创建子图时传入theta_offset之类的参数去调整起始方向不过新手不建议动它默认就很顺手。2.3 极径标尺与显示比例极坐标子图默认会显示同心圆虚线网格以及r轴的刻度标签。如果你的曲线只有一段范围比如r取值只在0到2之间建议手动设置r轴范围避免图像被大量留白占据ax.set_rlim(0, 3) ax.set_rticks([0, 1, 2, 3])把r轴上限压到比曲线最大r略大一点曲线看起来更充实。这里还要提醒一个坑如果你画的螺旋线r一路涨到100而你没设rlimmatplotlib会自动算范围但不同曲线混在一起时自动范围可能导致内圈细节被压缩成一团。所以多曲线对比时统一rlim是基本操作。2.4 删除默认刻度装饰让图片更干净论文插图或博客配图通常不需要那些密密麻麻的r轴同心圆和角度线。我的习惯是画完曲线后顺手做一次“瘦身”ax.set_yticklabels([]) ax.grid(True)如果你希望更极简可以ax.grid(False)但那样会丢失角度参考图形会显得空。折中方案是保留角度网格线删掉r轴数字标签只留同心圆虚线作为距离参考。这个细节我在导出插图时反复调整过最终发现“删标签、留网格”最耐看。3. 蜗线与心形线一个公式家族里的两个经典成员蜗线limaçon和心形线在数学上其实是一家人。心形线就是蜗线在特定参数下的特例。把这两个放在一起讲既能说明参数对曲线形态的深刻影响又能让大家少记一个公式。3.1 蜗线的公式与分类蜗线的极坐标方程是r a b * cos(θ)当b a时r始终为正曲线是一个没有内环的“带凹陷的圆”样子像蜗牛壳所以叫蜗线。当b a时r会出现负值曲线在θ对应的反向延长线上取点于是出现一个内环整条曲线变成一个外圈加内圈的形状有点类似“8”字被拉歪后的样子。我在实际画图时发现b a情况下如果直接用极坐标画matplotlib是支持负半径的它会自动把点画在角度反向的位置效果和数学书上的蜗线完全一致。这一点非常方便你不需要手动处理r的正负问题。3.2 心形线就是ab时的蜗线令a b 1方程变成r 1 cos(θ)这就是最经典的心形线。θ从0增加到π时cos从1降到-1所以r从2逐渐减小到0形成心尖θ从π到2π时r又从0升回2形成另一侧。整个图像左右对称顶部有点凹进去像一个倒置的心脏。我见过有些教程用r a(1 - cos(θ))画心形线这和r a(1 cos(θ))其实只差一个旋转方向前者心尖朝右后者心尖朝左。代码里选哪个都行只要你自己知道最终朝向就行。3.3 蜗线与心形线的完整绘图脚本下面这个脚本我加了b对a的比值系数方便你调节形态import numpy as np import matplotlib.pyplot as plt theta np.linspace(0, 2*np.pi, 1000) def draw_limacon(a, b, title): r a b * np.cos(theta) fig plt.figure(figsize(6, 6)) ax fig.add_subplot(111, projectionpolar) ax.plot(theta, r, linewidth2) ax.set_title(title) ax.set_rlim(0, 2.5) return ax draw_limacon(a1.0, b0.6, titleLimacon: b a (no loop)) plt.show() draw_limacon(a1.0, b1.0, titleCardioid: b a) plt.show() draw_limacon(a1.0, b1.5, titleLimacon: b a (inner loop)) plt.show()运行之后你会看到三张图第一张是一个圆润的“肾形”曲线第二张是心形第三张多了一个小内环。我觉得这种并排对比特别直观比自己对着公式想象要清晰得多。3.4 实操中关于极径范围的一个提醒蜗线在b a时r的最小值会小于0比如a1、b1.5时r_min -0.5。如果你用ax.set_rlim(0, 2.5)等于把负半径部分硬生生截掉了内环会消失大半。我第一次画的时候就是这个原因折腾了半小时才意识到是r轴下限设错了。所以画带内环的蜗线时rlim下限一定要设为负值比如ax.set_rlim(-1, 2.5)或者干脆不设让matplotlib自动算。自动计算在这种场景下通常比手动设更靠谱。4. 三叶与四叶曲线玫瑰线族里的奇偶差异三叶曲线和四叶曲线都属于玫瑰线族rose curve极坐标方程是r a * cos(k * θ)k是花瓣数相关的参数。这个家族特别有意思k为奇数时花瓣数等于kk为偶数时花瓣数等于2k。也就是说k3得到三叶草形k4却得到八瓣花。很多人第一次听到这个规律会觉得诡异我也一样。但结合r的正负变化就能秒懂。4.1 k3的三叶曲线是怎么变成三瓣的k3时r a*cos(3θ)。θ从0走到π/63θ从0走到π/2cos从1降为0所以r从a缩到0这是第一瓣的前半段。θ继续从π/6走到π/33θ从π/2走到πcos变成负值r为负于是点在相反方向上画出来形成了第二瓣的雏形。等θ扫完0到2π后你会发现一共形成了三瓣而且三瓣均匀分布夹角120度。这就是玫瑰线族的数学奥秘cos(3θ)在0到2π内完成了3个完整周期每个周期内先正后负正好撑出三瓣。4.2 为什么k4会画出八瓣而不是四瓣k4时r a*cos(4θ)在0到2π内cos(4θ)走完4个完整周期。每个周期依旧是先正后负按上面的逻辑4个正半周期产生4瓣4个负半周期还会产生4瓣总共8瓣。这就是为什么偶数k会让花瓣数量翻倍。如果你想要一个真正的“四叶草”形状四瓣就不要用k4而是用k2。r a*cos(2θ)才是经典的四叶曲线两正两负共四个花瓣分别朝向上下左右。4.3 三叶与四叶曲线的绘制代码一次画两幅子图做对比效果很直观import numpy as np import matplotlib.pyplot as plt theta np.linspace(0, 2*np.pi, 1500) fig, axes plt.subplots(1, 2, subplot_kw{projection: polar}, figsize(12, 6)) r3 1 * np.cos(3 * theta) axes[0].plot(theta, r3) axes[0].set_title(Three-petal rose) r2 1 * np.cos(2 * theta) axes[1].plot(theta, r2) axes[1].set_title(Four-petal rose (k2)) for ax in axes: ax.set_rlim(-1.2, 1.2) plt.tight_layout() plt.show()注意到我特意把rlim设成对称的(-1.2, 1.2)。如果你只设set_rlim(0, 1.2)负半周的部分会被截断三叶曲线直接缺花瓣。这个问题和蜗线内环截断是同一个原理吃透一次就能举一反三。4.4 花瓣之间的小尖角是怎么来的三叶玫瑰线的花瓣尖角位置在θ为0、2π/3、4π/3附近。这些尖角的产生是因为cos达到局部极值点1r最大曲线在那个方向最凸出。花瓣之间的凹陷则是r过零点导致的。如果你放大图片看细节会发现花瓣边缘不是圆弧而是带有微小曲率变化的曲线这恰恰是cos函数本身形态的体现。我有时候会把θ的采样点从1500降到200这时玫瑰线的花瓣边缘就会变成明显的折线形很丑。所以绘制这类含有尖锐过渡的曲线采样点一定要充足1000起步比较安全。5. 螺旋线让匀速旋转叠加线性外推螺旋线在工程里出镜率极高从蚊香盘到机械凸轮轮廓从蜗杆螺纹到螺旋天线设计图上到处都有它。用python画螺旋线几乎是每个工科学生都要过的坎。5.1 阿基米德螺旋线的数学描述最简单的螺旋线之一是阿基米德螺旋线极坐标方程为r a b * θθ从0开始匀速增大时r也匀速增大于是曲线像一圈圈等间距向外扩展的蚊香。这里的b决定圈与圈之间的间距b越大相邻两圈的间距越宽。注意θ的单位是弧度。如果你想画2圈θ要从0走到4π画n圈θ从0走到2πn。有时候我看到有人画螺旋线θ只取到2π画出来只有一圈还以为是公式写错了。其实不是公式错是圈数设置不够。5.2 画一个3圈阿基米德螺旋线的完整实例import numpy as np import matplotlib.pyplot as plt theta np.linspace(0, 6*np.pi, 2000) r 0.5 0.3 * theta fig plt.figure(figsize(6, 6)) ax fig.add_subplot(111, projectionpolar) ax.plot(theta, r, linewidth2) ax.set_title(Archimedean spiral (3 turns)) ax.set_rlim(0, 6) plt.show()这里r 0.5 0.3*thetaθ结束时6π约等于18.85r约为6.15所以我把rlim设到6。你可以根据自己的需求调整a或者b找到合适的疏密程度。5.3 对数螺旋线与黄金螺旋线的区别除了阿基米德螺旋线还有一类常见的对数螺旋线r a * exp(b * θ)它的特点是相邻两圈的间距按等比级数增加越往外间距越大。自然界里的鹦鹉螺外壳、台风云系结构大多更接近对数螺旋。而阿基米德螺旋线间距恒定更接近机械设计里等距槽的形态。二者选型完全取决于实际场景排线槽用阿基米德拟生态形态装饰用对数螺旋。下面这段代码可以并排对比两种螺旋theta np.linspace(0, 4*np.pi, 2000) r_arch 0.5 0.3 * theta r_log 0.5 * np.exp(0.1 * theta) fig, axes plt.subplots(1, 2, subplot_kw{projection: polar}, figsize(12, 6)) axes[0].plot(theta, r_arch) axes[0].set_title(Archimedean) axes[1].plot(theta, r_log) axes[1].set_title(Logarithmic) plt.show()对数螺旋线画起来要注意指数爆炸θ到4π时r已经变大很多图像中间那一圈会挤在一起其他部分则拉得很开。这是正常现象不要当成程序bug。5.4 绘制恒定线宽螺旋的一个小技巧如果你用ax.plot(theta, r)画螺旋线条宽度是恒定的这在一些装饰性绘图里不够生动。想要做出“从内到外变粗”的视觉效果可以分段多次plot每段设置不同线宽n_segments 20 turns np.linspace(0, 6*np.pi, n_segments 1) for i in range(n_segments): seg_theta np.linspace(turns[i], turns[i1], 100) seg_r 0.5 0.3 * seg_theta ax.plot(seg_theta, seg_r, linewidth0.5 i*0.2, colordarkblue)这种技巧在我以前给公司画装饰墙贴花纹时用到过效果比单线条螺旋生动得多且代码并不复杂只是多了一个分段循环。6. 进阶调整多曲线对比、动画与导出基础五种曲线画完如果你不想只停留在“能跑出图”的阶段可以考虑加几个我经常用的进阶操作。这些操作放在项目展示或论文插图里会明显提升观感。6.1 同一画布对比多种曲线把蜗线、心形线、玫瑰线、螺旋线画在同一张极坐标图上时最重要的是把rlim统一否则画出来的图会因为量纲差距太大而看不清细节。比如阿基米德螺旋线r动辄几十而心形线r最大只有2直接叠在一起心形线会被压成一个点。我的做法是只把“同量级”的曲线放一起对比。比如蜗线系列、玫瑰线系列分别画螺旋线单独成图。如果必须同框就分别归一化r r / r.max() * R把每条曲线的r都归一化到同一个最大半径R比如2这样结构比例还在但彼此又能看清。6.2 用matplotlib.animation让θ动起来动画有时候比静态图更能展示曲线的生成过程尤其是给初学者演示“θ扫描生成曲线”时。下面这个动画展示心形线从0度开始逐渐画出持续到一整圈from matplotlib.animation import FuncAnimation theta np.linspace(0, 2*np.pi, 300) r 1 np.cos(theta) fig plt.figure(figsize(5, 5)) ax fig.add_subplot(111, projectionpolar) line, ax.plot([], [], linewidth2) def update(frame): current_theta theta[:frame] current_r r[:frame] line.set_data(current_theta, current_r) return line, ani FuncAnimation(fig, update, frameslen(theta), interval20, blitTrue) ani.save(cardioid_animation.gif, writerpillow, fps30)把这段代码稍微改一改把r的表达式换掉就能生成任意一种曲线的生长动画。我给学生演示玫瑰线的时候这个动画一播几乎所有人都立刻理解了“花瓣是怎么被扫出来的”。6.3 导出矢量图与高清图的格式选择matplotlib的savefig在导出插图时非常实用。如果曲线要进入论文或印刷资料务必导出矢量格式比如PDF或SVG如果只是发博客或社交平台PNG高分辨率就够了。我常用的参数是plt.savefig(curves.png, dpi300, bbox_inchestight)有个很容易被忽略的细节bbox_inchestight会把周边空白裁剪掉让图片紧凑地贴着曲线区域。如果不加它保存下来的图片四周会有大片白边后期裁剪非常麻烦。6.4 中文字体与坐标轴标签的清理如果你在曲线图里加中文标题比如“心形线”直接set_title(心形线)大概率会显示为方块乱码因为matplotlib默认字体不支持中文。我的解决方案是plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, Noto Sans CJK SC] plt.rcParams[axes.unicode_minus] False第一行指定中文字体第二行让负号正常显示。axes.unicode_minus这个参数特别容易忘——中文环境下不设它为False坐标轴负号会显示成方块。这个坑我在帮别人调图时遇到不下三次。7. 常见报错与绘制陷阱排查写这种绘图脚本代码本身不长但坑却不少。我把最常见的几类问题整理出来方便你遇到时报错能快速对照。7.1 图出来了但是空的如果你的图形窗口弹出来里面什么都没有先检查ax.plot(theta, r)中的theta和r是否都是有限数值。最典型的错误是θ数组里有NaN或者r表达式写错导致全为NaN。我遇到过有人把cos写成np.cosin系统直接抛AttributeError也有人把θ设成了度数数组导致曲线被拉伸变形。一个快速定位方法在plot之前打印np.nanmin(r)和np.nanmax(r)如果输出nan说明r计算有问题如果数值正常再看θ的范围和分辨率。7.2 曲线有尖角或者断裂曲线看起来折来折去一般是采样点太少。θ只有几十个点时在曲率大的地方线段就会暴露出来形成折角。把np.linspace(0, 2*np.pi, 50)改成1000或2000就能解决。曲线断裂则通常是r出现无限大或NaN。比如对数螺旋线θ范围太大导致数值溢出r会变成inf线段就被截断了。解决办法是缩小θ范围或者限制r的最大值。7.3 多曲线叠加时颜色混乱当你在同一极坐标图上画多条曲线matplotlib会自动从默认颜色循环里分配颜色。如果你希望指定颜色比如蜗线全部用蓝色、玫瑰线用红色直接plot时传color参数最省心。另外plt.legend()在极坐标图上也能用但位置选择要留意默认可能挡住曲线。我一般设locupper right或者在极坐标图里把图例放在画布外ax.legend(loccenter left, bbox_to_anchor(1.05, 0.5))7.4 画出的心形方向不对这个不算bug更多是参考坐标系的选择问题。前面提到r 1 cos(θ)心尖朝左r 1 - cos(θ)心尖朝右。如果你需要心尖朝上可以对θ做偏移比如用r 1 - sin(θ)。很多人画完发现心形“躺倒”了其实就是没注意到极坐标默认0度在水平右方。想转成竖直方向数学上把cos换成sin就行不用硬调坐标轴。7.5 图片保存后边缘被裁剪保存的图看起来曲线被切掉一块十有八九是rlim设置过小或者图幅设置的figsize太扁。极坐标图比较适合正方形画布我一般用figsize(6, 6)。如果曲线最大值贴到边界把rlim上限上调10%曲线就能完全展示出来。8. 我踩过最深的坑负半径与极坐标网格线的纠缠最后单独写一节专门聊聊负半径。很多人第一次在极坐标里画玫瑰线或蜗线都会被“负半径”这个概念绕晕。极坐标里的r不一定是非负的。r cos(θ)在θ90度时r0在θ120度时r-0.5。数学上处理负半径的方法是当r为负时把点画在θπ方向、距离为|r|的位置。matplotlib在极坐标投影下自动实现了这个逻辑所以你不需要自己转正负。但正因为matplotlib自动处理有些时候反而容易迷惑。比如你设set_rlim(0, 1)后画r cos(2θ)负半周的点因为超出半径范围被丢弃曲线看起来就只剩一半花瓣。这种“花瓣无故消失”的现象90%都是rlim下限设错。我的建议是玫瑰线和内环蜗线一律把rlim下限设成负值或取消rlim限制让曲线完整展示。分清哪些曲线存在负半径是极坐标绘图里最核心的一个经验比记某个API参数重要得多。我在实际项目中画过一套“四叶曲线螺旋线”叠合的logo底纹当时就是因为内环被rlim截断前后折腾了很久。后来把所有绘制函数的rlim统一改成对称区间并加上自适应计算这些问题就再也没出现过。如果你也想做类似的项目建议把五种曲线的绘制封装成统一接口。比如写成draw_curve(ax, r_func, theta)的函数后面想加新曲线只需要传入一个r函数即可。保持简洁和可扩展这类绘图代码你写一次能反复用很多年。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑