资讯动态

LaTeX+pgfplots三维曲线绘制:从表达式到数据导入的完整指南

发布时间:2026/10/14 17:59:10 来源:尧图企业网站定制
如果你经常用 Matlab 或 Python 画三维图然后想把图放进 LaTeX 论文一定经历过这种别扭图是画出来了但坐标轴字体跟正文不是一个体系手动放大缩小后 PDF 又糊想换个视角还得回到原程序重新调参。后来我把 pgfplots 的 3D 绘制练熟了这类来回折腾基本消失了——直接在 .tex 文件里用代码定义三维坐标系、曲线、曲面和投影所有的字体、线型、颜色都跟文档自然统一改视角只需要改一个数字再重新编译。这篇文章就围绕 LaTeX pgfplots 绘制三维曲线图展开适合正在写论文、做实验报告、或者单纯想在文档里嵌一张高质量三维插图的读者。先说明一点pgfplots 不是唯一能画三维图的方案比如用 TikZ 手绘、或者先导出 PDF 再 includegraphics也都能用。但 pgfplots 的路数最接近用代码写图它的底层是 TikZ核心命令是 \addplot3胜在声明式、可复用、与 LaTeX 排版深度绑定。下面我会从环境配置讲到表达式绘图、数据文件导入再到一堆实际编译中遇到的坑尽量让你照着操作就能出图。1. 画图前先搞清楚axis 环境、view 视角和 addplot3 的三种数据喂法1.1 axis 环境是三维画布的根pgfplots 里一张三维图并不是一上来就画线它必须先建立一个 axis 环境。你可以把这个环境理解成一块三维画布画布的长宽由 width 和 height 控制坐标轴的范围可以用 xmin/xmax、ymin/ymax、zmin/zmax 显式指定也可以让 pgfplots 根据数据自动推断。三维图与二维图最大的差别是新增了 z 轴也新增了 view 选项——这是决定图好不好看的关键参数。\documentclass{standalone} \usepackage{pgfplots} \pgfplotsset{compat1.18} \begin{document} \begin{tikzpicture} \begin{axis}[ xlabel$x$, ylabel$y$, zlabel$z$, view{25}{30}, width10cm, height8cm, axis linesmiddle ] % 绘图命令写在这里 \end{axis} \end{tikzpicture} \end{document}代码里我写了 \pgfplotsset{compat1.18}很多人容易漏掉。compat 是 pgfplots 的兼容模式版本不同版本对坐标轴标签、图例、默认样式的处理方式会有差异。我不建议把它删掉或者写得很老否则明明照着教程写出来的图却跟教程不一样排查半天才发现是 compat 版本在作祟。我的习惯是在新文档的第一个导言区就固定一个版本比如 1.18然后再开始画图。axis 环境里还有一个容易忽略的点在三维图中width 和 height 控制的是整个坐标区域的尺寸而不是图片的边框。如果希望 label、刻度完全不挤占绘图区可以再加 scale only axis把 width、height 严格解释为坐标轴的尺寸。这个选项在插入双栏论文、需要严格控制插图大小时尤其有用。1.2 view 参数控制的是摄影师的位置view 的两个参数是方位角azimuth和仰角elevation。你可以想象一台摄像机以原点为中心做球面运动仰角负责上下调整镜头方位角负责绕着 z 轴旋转。默认的 view{25}{30}意味着你从西北偏上的方向看数据。这个视角对大多数旋转曲面都比较友好但也有例外。比如一条从 0 到 20 圈的螺旋线默认视角看起来会像一团乱麻这时候把仰角调到 60 度以上螺旋线的升距才看得出层次。反过来如果想看清曲面上某一条等值线的走向低一点的仰角会更合适。后面的实战部分我会给出更具体的参数搭配。1.3 addplot3 的三种数据喂法\addplot3 是 pgfplots 画三维图的主命令它和二维的 \addplot 区别就在于一个 3。但即便你知道这点第一次写还是会卡住因为 addplot3 支持三种数据喂法每种语法的写法略有不同。% 表达式方式参数方程 \addplot3[domain0:6*pi, samples500] ({cos(deg(x))}, {sin(deg(x))}, {x}); % 数据文件方式 \addplot3[blue, thick] table[xx, yy, zz] {data.csv}; % 坐标点方式 \addplot3[only marks] coordinates {(0,0,0) (1,1,1) (2,4,8)};这里有个很关键的小细节表达式方式的花括号不是好看的装饰而是用来区分三个分量的。如果写成\addplot3[domain...]{cos(deg(x)), sin(deg(x)), x};这种形式pgfplots 会把整个逗号分隔的内容解析成一个标量函数结果完全不是你要的曲线。正确写法是每个分量都单独用花括号包起来。喂法语法入口适用场景备注表达式\addplot3[...] ( {f}, {g}, {h} )数学公式直接生成的曲线/曲面参数方程最顺手数据文件\addplot3 table[...]实验数据、仿真输出支持 csv、dat 等纯文本坐标点列\addplot3 coordinates点数量少、临时画点不推荐数据量大时使用对大多数论文插图来说表达式适合展示理想模型table 适合展示真实数据coordinates 更多用来调试或画少量标记点。2. 用表达式直接画三维曲线从螺旋线到曲面投影2.1 空间螺旋线的标准写法先给一个可以直接编译的最小示例。这段代码画一条绕 z 轴上升的螺旋线也是我测试 pgfplots 三维功能时最常用的样例因为它同时涉及参数方程、三角运算、样本数控制和视角调整。\begin{tikzpicture} \begin{axis}[ view{40}{60}, axis linescenter, xlabel$x$, ylabel$y$, zlabel$z$, xtick\empty, ytick\empty, ztick\empty, width10cm, height8cm ] \addplot3[ domain0:6*pi, samples600, thick, blue ] ({cos(deg(x))}, {sin(deg(x))}, {x}); \end{axis} \end{tikzpicture}这里必须解释一个最容易踩的坑pgfplots 的三角函数默认以度为单位。如果直接写 cos(x)而 domain 给的是弧度 0:6pi它会把 6pi 这个数字当成度数去算 cos(18.8496°)这样螺旋线会完全不对——看起来像一条靠近原点的短弧线。解决办法有两种在表达式里手动写 cos(deg(x))把弧度换算成度或者在 axis 选项里加 trig format plotsrad让整个图都用弧度制。我推荐第二种因为它能让你不用在每个三角函数里都写 deg()。改完之后表达式变成({cos(x)}, {sin(x)}, {x})简洁很多。但要注意trig formatrad 只影响 addplot3 里的数学函数计算不影响 TikZ 其他部分的变换基本无副作用。samples 选多少也有讲究。6*pi 大约有 19 圈每圈至少要 20 个点才能让线型圆润所以 600 是比较稳妥的值。如果曲线更复杂可以再增加但 samples 过大会让编译时间明显上升PDF 体积也会暴涨建议在画面可接受的前提下尽量少。另外我不建议用 smooth 选项来补救样本不足——在三维曲线上 smooth 插值容易产生不自然的弯折宁可加几十个样本点。2.2 在同一个图里叠加投影曲线很多场景下光看三维螺旋线本身很难判断它在某个平面上的轨迹是否重叠、有没有周期性。这时候把曲线投影到 xy 平面或 xz 平面上会直观很多。投影的思路很简单把螺旋线的 z 分量改成 0就得到它在 xy 平面上的投影把 y 分量改 0就得到 xz 投影。把它们放进同一个 axis 里用灰色虚线区分。\addplot3[ domain0:6*pi, samples600, dashed, gray ] ({cos(deg(x))}, {sin(deg(x))}, {0});这样一张图里同时有空间曲线和它的平面投影既适合展示三维轨迹也适合做交叉验证。我在看粒子轨迹、飞行路径这类数据时几乎一定会加投影因为有些空间里很明显的规律俯视投影后会被遮挡平面上反而清晰。2.3 曲面和等高线投影的表现力说完了曲线顺带提一下曲面因为 pgfplots 里三维曲线和曲面常常混在同一张图里。画一个抛物面的最小代码是\addplot3[ surf, domain-2:2, samples35, shaderinterp ] {x^2y^2};surf 让它按曲面渲染shaderinterp 会让颜色渐变平滑而不是默认的网格色块。samples35 在 35×35 的网格下编译还算快PDF 也不算大。如果你只是想要一个示意曲面这个密度够了但要做精细渲染除非有足够的编译耐心否则不建议超过 50。如果要给曲面叠加等高线投影传统做法是借助 gnuplot 外部计算引擎\addplot3[ contour gnuplot{levels{-3,-2,...,3}, draw colorblack} ] {x^2-y^2};这个需要编译时加 -shell-escape 参数而且机器上必须装 gnuplot。如果你的环境没有 gnuplot可以退一步用view{0}{90}直接俯视得到等值线效果虽然不是严格的等高线但视觉上已经接近。3. 论文级外观调整view 视角、坐标轴样式与 colormap3.1 调视角几个我常用的数值组合画三维图和二维图最大的不同就是怎么看常常比画什么更影响效果。同一组数据换个 view 可能从一团乱麻变成清晰的层次图。下面是我在几种常见场景下反复试出来的视角组合使用场景view 参数理由螺旋线、空间轨迹{30}{60} 或 {40}{50}能看到升距和圈数旋转曲面{20}{25}保留侧面轮廓接近默认视角俯视等值线{0}{90}正上方俯视类似二维等高线有纵深的点云{135}{20}拉开深度感避免点叠在一起这些数值不是标准答案只是起点。我在实际画图时通常先编译一个默认视角然后一次性写好几个 view 候选值通过改数字快速对比挑出信息量最大、遮挡最少的一个。记住三维图的价值是把数据关系讲清楚而不是一味追求酷炫的斜视角。3.2 坐标轴中间线还是边框两种风格怎么选axis linesmiddle 和 axis linesbox 是两种最常用的坐标轴样式。用 middle 时三个轴交叉于原点适合数据在原点附近分布、有正有负的情况用 box 时坐标轴构成一个包围盒刻度在盒子边缘适合数据整体位于第一象限、或者数据范围很大时。我的习惯是数据围绕原点分布用 middle数据集中在某个象限用 box。如果担心轴线穿过数据本身影响阅读还可以用 axis line style{drawnone} 把轴线隐藏掉只保留刻度线这种处理在论文插图里很常见。有个细节容易被忽略zlabel 在 middle 模式下默认会压在 z 轴上跟数字标签挤在一起。可以加zlabel style{rotate-90}让它顺着轴方向排版或者在必要的时候手动调整位置。虽然有点繁琐但字符重叠这种小问题在提交论文时经常被审稿人挑出来。3.3 刻度、网格与 colormap 的配合默认情况下三维图会带 x 轴和 y 轴的刻度z 轴刻度有时会被省略。如果你觉得刻度太乱可以直接写xtick\empty, ytick\empty, ztick\empty来全部关掉只留下坐标轴。反过来如果想强调曲面的高低变化可以用gridmajor打开网格线让空间位置更有参照。颜色方面pgfplots 自带了不少 colormap我最常用的是 colormap/viridis 和 colormap/hot。viridis 的优点是色觉友好、黑白打印后灰度层次也清楚适合曲面和散点hot 则适合表示强度场。引用方式很简单\addplot3[ surf, colormap/viridis, shaderinterp ] {x^2 - y^2};如果想自定义颜色可以在导言区用 \pgfplotsset 定义\pgfplotsset{ colormap{mybw}{ color(0)(black) color(1)(white) } }然后写 colormap namemybw 或者直接 colormap/mybw。对于科研插图我建议灰度或单一色系优先彩色放在需要强调的系列上这样即使打印成黑白的图仍然能读。4. 真实数据变成三维图外部数据文件的导入与多条曲线对比4.1 table 数据格式与读取方法表达式绘图适合展示理论模型但真正的实验数据、仿真结果绝大多数时候都在文件里。pgfplots 可以直接读取纯文本表格这是它比很多 GUI 绘图软件更适合写论文的原因之一——数据更新了重新编译一下就能看到新图过程完全可复现。假设你有一个 data.csv内容大概长这样X,Y,Z -2.00, -2.00, 8.00 -1.80, -2.00, 7.24 ...读取它的标准写法是\addplot3[ surf, mesh/orderingy varies ] table[xX, yY, zZ] {data.csv};如果表头列名不可用也可以用 x index0, y index1, z index2 按列序号读取。逗号分隔的文件要加 col sepcomma默认情况下 pgfplots 把空格或 tab 当作分隔符。这里容易踩的坑是文件路径里带了中文字符或空格在部分配置下读不到文件建议统一放在英文路径下或者用相对路径。4.2 从 Python / Matlab 导出数据时的实操细节用 Python 导出三维曲面数据时最容易出现的问题就是网格不完整。如果直接用循环把散点写成 CSV表面看还行但 pgfplots 的 surf 要求数据按行列规则排列否则会出现大量裂缝。标准的做法是用 numpy.meshgrid 生成规则网格然后按行拉平import numpy as np x np.linspace(-2, 2, 40) y np.linspace(-2, 2, 40) X, Y np.meshgrid(x, y) Z np.cos(X) * np.exp(-0.2 * (X**2 Y**2)) with open(data.csv, w, encodingutf-8) as f: f.write(X,Y,Z\n) for xi, yi, zi in zip(X.ravel(), Y.ravel(), Z.ravel()): f.write(f{xi}, {yi}, {zi}\n)数据点密度需要控制。三维曲线几千行没什么问题但曲面网格建议 40×40 以内如果到了 100×100编译时会非常痛苦生成的 PDF 也会巨大。数据量实在太大时建议先降采样再画图而不是让 pgfplots 硬扛。如果数据里出现 NaNpgfplots 会默认为空点并跳过曲面会出现窟窿。这在某些场景下是优点——比如表示测量失败的区域——但如果你不想要窟窿就得提前把 NaN 填充成某个合理值比如相邻点的平均。4.3 同一图内多条曲线对比与配色论文里很常见的需求是把理论曲线和实测曲线放在同一张图里让读者自己看差异。pgfplots 做这件事非常自然只要在同一个 axis 里依次写多个 \addplot3并在图例中标注即可。\addplot3[ domain0:6*pi, samples600, thick, blue ] ({cos(deg(x))}, {sin(deg(x))}, {x}); \addlegendentry{理论轨迹} \addplot3[ blue!40, only marks, mark*, mark size2pt ] table[xX, yY, zZ] {measured.csv}; \addlegendentry{实测采样点}这里有一个三维图特有的问题默认的 legend 位置在坐标系内部但三维图的坐标区域通常不会给图例留太多空白。所以我会显式指定图例位置让它靠近图形右上角但不遮挡数据legend style{at{(0.97,0.97)}, anchornorth west, drawnone}配色上建议同一张图里不要超过三种主色。理论曲线用实线深色实测数据用浅色散点这样即使只印灰度版也能分清。如果你要让曲线在 z 方向上渐变变色可以给 \addplot3 加上 point meta 参数让它按 z 值或某一列数据着色。这个技巧在需要强调高度或强度时非常有用\addplot3[ mesh, point meta\thisrow{Z} ] table[xX, yY, zZ] {data.csv};mesh 会让曲线分段着色point meta 决定颜色沿着哪个量变化。这种渐变除了好看还能在不额外增加文字说明的情况下让读者直接看到数据沿轴的走势。5. 编译慢、中文乱码、视图失灵一圈避坑记录5.1 externalize 的收益与坑三维图和复杂二维图一样最大的敌人是编译时间。如果你的文档里有几十张图每编译一次都重画所有图状态好时等一分钟状态不好时等三分钟都正常。pgfplots 提供了外部化机制可以把每张 tikzpicture 单独编译成 PDF 并缓存下次编译只有改动过的图才会重画。启用方式也简单\usetikzlibrary{external} \tikzexternalize[prefixtikzcache/]但我要提醒几个坑。第一externalize 在某些宏包或命令下会失效最常见的症状是图的位置变成空白只留下一个空文本框。如果你遇到这种问题检查是不是用了 \input 或其他动态内容。第二外部化的 PDF 裁剪可能不精确导致图边缘被裁掉一部分解决办法是在 tikzpicture 外面加一个 \begin{pgfinterruptpicture} 之类的不多更实用的是直接给 axis 加足够大的 width/height留出边距。第三如果图里定义了随机数据或带有时间戳的变量外部化缓存会让内容不更新造成改了参数图没变的错觉。如果你只想把图单独编译不想跟主文档混在一起还有一个轻量方案用 standalone 文档类写一个 .tex 文件只包含单张图然后编译成 PDF 再 \includegraphics。这样每张图都是独立文件主文档编译速度很快图本身也很干净。5.2 中文标签要不要换引擎pgfplots 本身不直接处理字体坐标轴的文字都交给 LaTeX 排版。如果文档用 pdflatex 编译写上中文 xlabel 大概率会报错或出现乱码因为默认字体不含中文字形。最省事的方案是直接切换到 XeLaTeX 或 LuaLaTeX搭配 ctex 文档类\documentclass{ctexart} \usepackage{pgfplots} \pgfplotsset{compat1.18} ... \begin{axis}[ xlabel{时间 t}, ylabel{温度 T}, zlabel{高度 H} ]用 XeLaTeX 编译后中文标签就能正常显示字体也可以直接用系统字体。这一步会改变整个文档的编译方式建议在项目一开始就确定好引擎而不是等图都画完了再切换否则后续排查会非常痛苦。如果你坚持用 pdflatex又确实需要中文标题也不要硬刚。我的做法是图内坐标轴全部用英文符号或公式比如时间 t、温度 T中文说明放到 caption 里反而更符合科技论文的排版习惯。5.3 常见报错速查说人话的修复清单我在折腾 pgfplots 三维图的过程中遇到过不少次编译失败或效果离谱的情况下面是我整理的快速排查表现象常见原因解决办法Missing number, treated as zero三角函数没转弧度或表达式分量忘了用花括号包住加 trig formatrad或给每个分量加 {}画出来的图是平的 / 一根线用了 \addplot 而不是 \addplot3或忘记设置 view确认命令是 \addplot3曲面出现大量裂缝数据文件网格不完整或里面有 NaN用 np.meshgrid 重新生成规则网格编译超慢、PDF 巨大samples 过大或数据点太多降低 samples或对数据降采样图例挡住主要数据legend 默认位置在坐标系内部显式指定 legend style 的 at 和 anchor中文 label 乱码pdflatex 默认字体不含中文字形换 XeLaTeX ctex或改用英文标签图上所有刻度消失误把 xtick 设为空或 ztick 没设置检查是否写了 xtick\empty 等选项这些坑并不难解决难的是第一次遇到时不知道问题出在哪一层。我个人的经验是先排除语法层面——确认是不是 addplot3、确认分量有没有花括号、确认 deg 有没有处理好再看数据层面——文件路径、列名、分隔符最后才考虑效果层面——视角、图例、颜色。按这个顺序排查绝大多数问题都能在几分钟内定位。最后说一个我实际画图时的小习惯每次建新图之前我会先用最少代码跑一个裸的 axis 环境也就是只有坐标轴、没有数据的空图确认编译链路和字体没问题之后再加曲线和数据。这个习惯帮我避开了很多图复杂到没法排查的场面。三维图跟二维图不一样参数多了一个维度出错面也跟着多了一维先在最小环境下打底再逐层叠加反而最高效。

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

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

免费获取报价 →
↑