
简介MATLAB NURBS工具箱是一套面向MATLAB环境的专业扩展库专注于非均匀有理B样条NURBS曲线与曲面的创建、编辑和可视化适用于计算机图形学、CAD/CAM/CAE以及科研数据分析等场景能帮助工程师、科研人员和学生高效处理复杂几何建模任务。压缩包共含60个文件主体为59个M脚本文件涵盖曲线曲面构造、控制点修改、节点插入、阶数提升、插值拟合、修剪旋转以及常见格式导出等功能另含1个TXT授权说明包体仅44KB结构紧凑、易于阅读。目前已有452人下载学习实用性得到了初步验证。借助该工具箱使用者可以快速上手NURBS建模与计算通过内置可视化函数观察曲线曲面形态结合插值与拟合工具完成数据建模并从源码入手理解算法本质资源内的演示脚本也能降低学习门槛便于后续二次开发与算法研究。1. 为什么拿到 MATLAB NURBS 工具箱.zip 别急着 addpath看到“MATLAB NURBS 工具箱.zip”这个压缩包先别把它当成普通函数库解压后 addpath 完事。这个工具箱的核心不是某个.m文件而是一套以节点向量knot vector、控制点、权重三个数组为中心的 B 样条数据结构你调用nrbmak时参数顺序写错后面所有nrbeval、nrbdegelev都会跟着出错。它解决的是非均匀有理 B 样条在 MATLAB 里的标准操作求值、求导、升阶、插节点、构造旋转曲面。适合正在做几何建模、逆向工程、CNC 轨迹规划或机器人路径生成的工程师也适合想用曲线拟合替代多项式回归的研究生。先把结构与数学模型对齐再谈功能。2. NURBS 工具箱的数据结构节点向量、控制点与权重怎么对齐2.1 为什么 NURBS 曲线不能只看控制点坐标一只完整的 NURBS 曲线在工具箱里被表示成一个struct而不是直接保存一批采样点。控制点坐标决定了曲线的“大致位置”但真正影响曲线连续性和局部支撑关系的是节点向量。三次 B 样条的每个控制点只影响一段参数区间这个区间由节点向量中的重复度决定如果节点向量写错曲线形状即使看起来正常升阶或插节点后也会出现不可控的扰动。权重体现在齐次坐标中每个控制点实际保存成[w*x; w*y; w*z; w]四行。权重的比值决定曲线被某个控制点拉近的程度而不是权重绝对值。比如权重从 1 改成 10会把曲线明显拉向该控制点但两侧远离该点的区段几乎不变。理解这一点后你才不会在修改曲面时只是想当然地改某个坐标。2.2 nrbmak 的输入顺序和一条可复现的三次曲线构造 NURBS 曲线的标准入口是nrbmak(coefs, knots)。coefs是 4 行 n 列矩阵每列是一个控制点的齐次坐标knots是长度等于n order的非降序列order degree 1。下面这段代码在 MATLAB 里可以直接运行% 5 个控制点三次曲线控制点数 5 阶数 4 9 个节点 P [0 0 0; 1 2 0; 2 2 0; 3 1 0; 4 0 0]; % 转置后是 3 x 5 k [0 0 0 0 0.5 1 1 1 1]; % 两端重复 4 次形成 clamped coefs [P; ones(1, 5)]; % 最后一行是权重默认全 1 crv nrbmak(coefs, k); t linspace(0, 1, 200); [pos, wts] nrbeval(crv, t); plot3(pos(1, :), pos(2, :), pos(3, :), LineWidth, 1.5);coefs最后一行的ones(1,5)表示所有权重为 1此时 NURBS 退化成标准 B 样条。knots的[0 0 0 0 ... 1 1 1 1]写法让曲线强制经过首尾控制点这是最常见的外观要求如果写成[0 0.2 0.4 0.6 0.8 1]这类均匀节点曲线两端会“浮起来”不经过端点。nrbmak对节点数量和阶数有强检查长度对不上会直接报错因此在做批量生成时先计算好length(knots) size(coefs,2) order是第一步。这里顺带记一下结果结构里常用的字段含义字段含义取值示例crv.order阶数等于次数 14 代表三次crv.knots节点向量非降[0 0 0 0 0.5 1 1 1 1]crv.coefs4 行 n 列齐次控制点末行是权重crv.number控制点数量5这个表和nrbmak的输入直接对应排查问题时先打印这四个字段能快速定位是节点数量不对还是coefs里混入了普通坐标。实际操作里我见过不少同事把coefs写成 3 行 n 列结果 MATLAB 直接提示维度不匹配。记住工具箱内部所有计算都依赖齐次坐标哪怕权重全 1也必须补足第四行。2.3 权重改到 10曲线会被拉到哪个方向把中间控制点的权重从 1 改成 10再用同样节点向量求值能直观看到权重的局部作用wts [1 1 10 1 1]; crv_w nrbmak([P; wts], k); [pos_w, wts_w] nrbeval(crv_w, linspace(0, 1, 200)); figure; plot(pos(1, :), pos(2, :), b-, LineWidth, 1.2); hold on; plot(pos_w(1, :), pos_w(2, :), r--, LineWidth, 1.5); plot(P(1, :), P(2, :), ko); hold off;这里第二个wts是求值返回的权重不是输入权重为了可读性输入侧变量名建议改成weight_in或wts_in。权重 10 的控制点会把曲线明显拉向第三个控制点但两侧远离该点的区段几乎不变。权重大于 0 是前提权重为 0 等价于隐藏该控制点权重为负则可能导致曲线反转除非做特殊造型否则不要碰。3. 把 zip 里的 NURBS 工具箱装进 MATLAB解压、路径与版本冲突3.1 解压前先测 zip 完整性避免无效归档压缩包本身就该被当成交付物来验收。在解压前先跑一下完整性检查能省掉后面所有Undefined function or variable的排查时间。Linux 或 macOS 下用unzip -t MATLAB_NURBS_Toolbox.zipWindows 10 之后自带tar也可以直接tar -tf MATLAB_NURBS_Toolbox.zip列出内部文件名如果这一步出现error read zip archive或者invalid zip archive: could not find eocd先别急着改代码重新下载压缩包再试。EOCD 错误基本就是文件没下完整或者杀毒软件在下载过程中锁了文件。还有一个常见场景是在 MATLAB 内解压时被杀软扫描占用了文件句柄导致只解出一半文件这时退出 MATLAB 再解压通常能恢复。在 MATLAB 里解压也很简单unzip(MATLAB_NURBS_Toolbox.zip, D:\matlab_tools);解压后先看顶层目录里是否包含nrbmak.m、nrbeval.m、nrbdegelev.m、nrbkntins.m、nrbplot.m、nrbrevolve.m。有的网站在打包时只截取了inst子目录缺了示例但函数还在如果连nrbmak.m都不在说明压缩包本身不完整换一个镜像源。常见做法是先dir(D:\matlab_tools\nurbs_toolbox\*.m)数一下文件数量再进入下一步路径配置。这个习惯能帮你区分“工具箱没装上”和“zip 下载不全”两类问题避免把时间浪费在路径调试上。3.2 三条把工具箱永久加进 MATLAB 搜索路径的写法只把 zip 解压到硬盘不会让 MATLAB 认识这些函数。我一般这样处理当前路径addpath(D:\matlab_tools\nurbs_toolbox); savepath;addpath只对当前会话生效savepath会把当前路径写入pathdef.m下次启动仍然保留。如果工具箱里还有examples、inst这类子目录可以用addpath(genpath(D:\matlab_tools\nurbs_toolbox))但要注意genpath会把隐藏目录、.git都加进去工业电脑上不建议图省事。更稳妥的做法是明确列出两个子目录或者只加顶层再加你确实需要的子目录。放 Linux 上时另一种方式是启动前写环境变量export MATLABPATH~/matlab_libs/nurbs_toolbox matlab -desktop三种方式选一种即可。刚装完 MATLAB R2023b 的人最容易漏掉环境变量这步以为安装教程走完就结束了。配置完成后用which nrbline -all检查如果输出里同时出现两个不同路径说明以前装过旧版 NURBS 工具箱必须清理其中一个否则 MATLAB 按路径顺序优先加载先找到的那个可能出现nrbplot版本混用导致的绘图崩溃。方法命令适合场景临时addpath(路径)一次会话里快速验证永久addpath(...); savepath;个人开发机环境变量MATLABPATH... matlab -desktopLinux 无 root 权限如果savepath因为权限问题失败可以指定保存位置例如savepath(fullfile(userpath,pathdef.m))然后把userpath确认一下。MATLAB 启动时会依次读取系统pathdef.m和用户pathdef.m后者优先所以即使你不是管理员也能把工具箱路径锁到自己的账号下。3.3 最小验证一条直线确认 nrbline 已生效路径配好后的最小验证不需要立即跑曲面先建一条直线即可seg nrbline([0 0 0], [1 1 0]); nrbplot(seg, 20);如果这条直线能画出来说明工具箱主目录已被正确加载。接着运行which nrbrevolve确认扫掠相关函数也在。R2016b 到 R2023b 的主版本都兼容但要注意新版 MATLAB 对某些绘图函数有重载which nrbplot -all如果出现两个结果优先保留工具箱路径靠前的那个。这个最小脚本可以存成startup_verify.m以后重装环境直接跑一次即可。4. 用 nrbmak 建模、nrbkntins 精修、nrbrevolve 扫掠曲面4.1 用 nrbeval 把曲线变成可离散的点云第 2 章已经用nrbeval出过点这里展开讲它的返回结构tt linspace(0, 1, 200); [pos, wgt] nrbeval(crv, tt); plot3(pos(1, :), pos(2, :), pos(3, :), b-);tt的取值范围必须是节点向量的闭区间通常取[0,1]。pos是 3 行 n 组点wgt是 1 行 n 组的权重注意wgt不是齐次坐标的第四行而是求值得到的标量权重绘制时只用pos。想验证某一点的曲线位置可以取tt0.5再和手工代入 NURBS 公式的结果对比节点向量内的重复度越大该处的连续阶数越低tt落在重复节点上时求值结果仍然稳定。这里有一个实际使用中容易踩的坑nrbeval对输入是行向量还是列向量不敏感但输出的维度会跟着变为了下游统一我习惯始终用linspace生成行向量。如果你在循环里逐点调用nrbeval(crv, t0)得到的是单点循环 100 次的时间和一次传 100 个点差了不只一个数量级。批量求值是把这个函数用快的先决条件。4.2 用 nrbdegelev 升阶、用 nrbkntins 插入节点曲线阶数和节点密度是两个不同的自由度。升阶改变多项式的最高次数但不改变几何形状插节点在保持形状的前提下增加控制点数量为局部精细修改提供更多自由度。下面这两行是调试周期里最常背下来的crv2 nrbdegelev(crv, 1); % 从三次升到四次 crv3 nrbkntins(crv2, [0.4 0.7]); % 在 0.4 和 0.7 位置插入节点nrbdegelev的第二个参数是升高的阶数不是目标阶数要升到 5 次传 2 即可。nrbkntins的第二个参数是新增节点列表必须落在当前节点向量的开区间内如果你想把某处做成 C0 尖角就让同一个节点值重复插入order-1次。控制点数量变化可以这样观察before crv.number; after crv3.number; fprintf(控制点数量%d - %d\n, before, after);升阶后nrbdegelev生成的coefs会多出若干列这些列不是简单插值得到的是算法按基函数转换重算的结果。如果你发现升阶后曲线偏移超过 1e-10说明工具箱的初始化不干净或你手动改过crv.knots而没同步crv.coefs。另一个判断技巧是看crv.order在升阶后是否等于原值加增量有些旧版本工具包对order和degree的命名不统一确认该字段能省很多误解。函数作用关键参数nrbdegelev(nurbs, n)阶数升高 nn 是增量nrbkntins(nurbs, knt)插入多个节点knt 必须在开区间内nrbeval(nurbs, tt)参数求值tt 在定义域内nrbrevolve(curve, pnt, vec, th)绕轴扫掠成曲面th 是弧度用一个典型的叶片轮廓验证先在0.4处插入节点再合并升阶比先升阶再插节点计算量更小数值也更稳。原因是高次基函数对节点位置变化更敏感后插节点容易让局部曲率出现振荡。如果你有两个模型需要做布尔运算建议先统一阶数再导出否则两边阶数不一致会导致裁剪结果出现微小的边界缝隙。4.3 把曲线绕轴扫掠成旋转曲面曲面构造里最常用的是旋转扫掠。nrbrevolve接受一条曲线、轴上的点、轴方向和旋转角度返回一个 NURBS 曲面。以下代码把第 2 章的三次曲线绕 z 轴旋转 90 度axis_pt [0 0 0]; axis_v [0 0 1]; srf nrbrevolve(crv, axis_pt, axis_v, pi/2); [pts, wt] nrbeval(srf, {linspace(0, 1, 30), linspace(0, pi/2, 30)}); surf(squeeze(pts(1, :, :)), squeeze(pts(2, :, :)), squeeze(pts(3, :, :)), ... FaceColor, interp);注意nrbeval对曲面的第二个参数是元胞数组两个成员分别是 u 和 v 方向的参数向量。返回的pts是3 x m x n直接传给surf前必须用squeeze去掉单例维度。axis_pt和axis_v顺序写反是最常见的错误axis_pt是轴上任意一点axis_v是方向向量二者不能互换。theta用弧度制转 360 度时直接传2*pi会出现一个重叠的接缝我一般先对曲线在起点做一次nrbkntins再扫掠避免接缝处出现过密的重合点。5. 用 nrbderiv/nrbdeval 做曲率核查并把结构以 .mat 导出给 Python5.1 检查导数曲线而不是直接 diff离散点的差分只能看个大概真正做机械加工或运动规划时导矢和二阶导矢都要用解析结果。工具箱里nrbderiv生成导数曲线的 NURBS 结构再交给nrbdeval与原曲线一起求值dnurbs nrbderiv(crv); [pt, dpt, ddpt] nrbdeval(crv, dnurbs, linspace(0, 1, 200)); curv abs(dpt(1, :) .* ddpt(2, :) - dpt(2, :) .* ddpt(1, :)) ./ ... (dpt(1, :) .^ 2 dpt(2, :) .^ 2) .^ (3 / 2);dpt是一阶导矢ddpt是二阶导矢上面的曲率公式只适用于平面曲线三维曲线要改用norm(dpt).^3和向量叉积。nrbderiv可以预先缓存导数结构之后任意参数点都只需调用nrbdeval适合在循环里批量采样。如果某段曲率出现连续尖峰优先检查该处节点的重复次数C0 连续点的曲率本来就不连续这不是求解错误。导数结构里order比原曲线低 1你可以把它当作一条新的 NURBS 曲线来继续升阶或插节点。5.2 把 crv 结构存成 .mat交给 Python 读取需要把曲线交给你自己写的 C 求解器或 Python 后处理时直接保存crv这个 struct 是最省事的方案save(nurbs_demo.mat, crv, -v7);这里用-v7而不是默认-v7.3是为了让 Python 侧能通过scipy.io.loadmat直接打开。.mat文件除了用 MATLAB 打开最常见的替代就是用scipy.io读取但-v7.3默认压缩成 HDF5需要改用h5py才能处理。Python 侧解析代码from scipy.io import loadmat d loadmat(nurbs_demo.mat)[crv][0, 0] knots d[knots].ravel() coefs d[coefs].T order int(d[order].ravel()[0])读取后coefs每行对应一个控制点第四列是权重按[x, y, z, w]的顺序排布。这样把 knots 和 coefs 透传出去后你在 Python 里做非均匀采样或碰撞检测就不需要回到 MATLAB 重新导一遍点数据。本文还有配套的精品资源点击获取