1. 项目概述从玩具到数学的奇妙旅程万花尺这个很多人童年记忆里的绘图玩具由一个带齿的大尺环和一个小齿轮笔套组成。转动小齿轮在大尺环内滚动笔尖就能画出各种繁复而规律的曲线。小时候只觉得它画出的图案很“魔法”直到后来学了数学和编程才明白这背后藏着一门优雅的几何学——圆内旋轮线。用Matlab来仿真万花尺本质上就是用代码去复现和探索这种几何运动把物理玩具的随机性变成可控、可预测、可无限扩展的数字艺术。这不仅仅是一个怀旧项目更是理解参数化曲线、运动合成以及异形齿轮啮合原理的绝佳实践。对于工科生、数学爱好者或者任何对创意编程感兴趣的人来说通过Matlab实现它既能巩固坐标系变换、向量运算等基础知识又能亲手创造出令人惊叹的视觉图案成就感直接拉满。2. 核心数学原理圆内旋轮线方程推导要仿真首先得知道笔尖到底是怎么动的。这需要从几何上严格推导出笔尖的轨迹方程。2.1 建立运动模型我们假设大固定圆的半径为 ( R )小滚动圆相当于万花尺的小齿轮的半径为 ( r )。小圆在大圆内部紧贴内壁做纯滚动无滑动。在小圆上固定一个点 ( P )相当于笔尖的位置它到小圆圆心的距离为 ( d )( d ) 可以小于、等于或大于 ( r )。我们的目标就是求出点 ( P ) 在大圆固定坐标系下的坐标 ((x, y)) 随时间 ( t ) 变化的参数方程。这里的关键是理解两个圆的角速度关系。由于是纯滚动两圆接触点的线速度必须相等。设小圆圆心绕大圆圆心转动的角速度为 ( \omega )这是我们的自变量通常令 ( \omega 1 ) 来简化那么小圆绕其自身圆心旋转的角速度 ( \omega‘ ) 是多少考虑初始时刻两圆在右侧某点接触。当小圆圆心绕大圆圆心转过角度 ( \theta ) 时小圆与固定大圆的接触点也发生了变化。由于纯滚动小圆上原本与固定点接触的那一点所滚过的弧长必须等于大圆上被滚过的弧长。这个关系推导下来会得到小圆自身的旋转角度 ( \phi ) 与 ( \theta ) 满足 [ \phi \frac{R - r}{r} \theta ] 注意因为是在内圆滚动小圆自转的方向与其公转方向是相同的。这个公式是理解整个运动的核心。2.2 坐标变换与参数方程现在我们在固定平面建立坐标系原点 ( O ) 在大圆圆心。小圆圆心 ( C ) 的坐标它在大圆内距离原点为 ( R - r )。当公转角度为 ( \theta ) 时其坐标为 [ C_x (R - r) \cos\theta, \quad C_y (R - r) \sin\theta ]笔尖 ( P ) 相对于圆心 ( C ) 的坐标在小圆自身的坐标系中随小圆旋转笔尖 ( P ) 到圆心 ( C ) 的距离为 ( d )。假设初始时刻( \theta 0 )笔尖位于小圆上相对于圆心 ( C ) 的某个方向例如正右方。那么在小圆自转了角度 ( \phi ) 后笔尖相对于圆心 ( C ) 的坐标为 [ P‘_x d \cos\phi, \quad P‘_y d \sin\phi ] 这里假设初始相位为0实际上可以加一个初始相位角 ( \delta ) 来产生更多变化。合成得到绝对坐标将相对坐标通过旋转和平移变换到固定坐标系。但这里要注意小圆自身的坐标系随 ( C ) 点平动并旋转其x轴方向并不是固定的。更简单的方法是笔尖 ( P ) 可以看作是从大圆圆心 ( O ) 到小圆圆心 ( C )再加上从 ( C ) 到笔尖 ( P ) 的向量。而从 ( C ) 到 ( P ) 的向量其方向由公转和自转共同决定。一种清晰的推导思路是笔尖 ( P ) 的位置可以视为两个旋转运动的叠加。最终我们得到圆内旋轮线内摆线的标准参数方程 [ x(\theta) (R - r) \cos\theta d \cos\left(\frac{R - r}{r} \theta\right) ] [ y(\theta) (R - r) \sin\theta - d \sin\left(\frac{R - r}{r} \theta\right) ] 这里 ( y ) 方程中的负号很关键它源于内滚动的几何关系以及坐标系y轴向上为正的约定。如果小圆在大圆外部滚动外摆线这个符号会变成正号。参数 ( \theta ) 就是公转角度也是我们的时间变量。注意方程中的 ( d ) 是笔尖到小圆圆心的距离。当 ( d r ) 时笔尖就在小圆的圆周上画出的是标准内摆线。当 ( d r ) 时笔尖在小圆内部曲线更平滑当 ( d r ) 时笔尖在小圆外部曲线会出现回环和尖点图案更为复杂这也是万花尺能画出星形、花瓣形图案的奥秘所在。3. Matlab仿真实现从方程到动画理论方程有了用Matlab实现就是水到渠成的事情。我们的目标是写一个脚本不仅能画出最终的静态曲线还能动态展示小圆滚动和笔尖画图的过程这样理解起来更直观。3.1 基础静态绘图我们先从画出一条静态曲线开始。假设我们选择一组参数( R 5 ), ( r 2 ), ( d 1.5 )。我们需要生成从 ( \theta 0 ) 到某个终止角度例如 ( 10\pi )的一系列点。% 万花尺圆内旋轮线基础仿真 - 静态图 clear; clc; close all; % 1. 定义参数 R 5; % 大圆半径 r 2; % 小圆半径 d 1.5; % 笔尖到小圆圆心的距离 n_cycles 10; % 小圆公转的圈数相对于大圆 theta_end n_cycles * 2 * pi; % 总旋转角度 % 2. 生成参数序列 num_points 5000; % 采样点数量越多曲线越光滑 theta linspace(0, theta_end, num_points); % 3. 计算旋轮线坐标 % 注意这里使用了标准的圆内旋轮线参数方程 x (R - r) * cos(theta) d * cos((R - r)/r * theta); y (R - r) * sin(theta) - d * sin((R - r)/r * theta); % 4. 绘图 figure(‘Position‘, [100, 100, 800, 800]); plot(x, y, ‘b-‘, ‘LineWidth‘, 1.5); axis equal; % 保证x, y轴比例相同图形不变形 grid on; title([‘万花尺图案 (R‘, num2str(R), ‘, r‘, num2str(r), ‘, d‘, num2str(d), ‘)‘]); xlabel(‘X‘); ylabel(‘Y‘);运行这段代码你就能看到一条精美的、闭合的如果参数比值为有理数曲线。尝试修改R,r,d和n_cycles的值你会发现图案千变万化。当(R-r)/r是一个有理数即两个整数之比时曲线是闭合的且其花瓣或循环的数量与这个分数有关。如果是无理数曲线将永不重复填满整个环形区域。3.2 动态仿真与动画制作静态图好看但动态过程更能揭示机理。Matlab的动画功能可以很好地展示这一点。% 万花尺圆内旋轮线动态仿真 clear; clc; close all; % 1. 定义参数 R 5; r 2; d 1.5; n_cycles 5; theta_end n_cycles * 2 * pi; num_frames 300; % 动画帧数 theta_anim linspace(0, theta_end, num_frames); % 2. 预计算所有帧的数据提升效率 x_trace []; % 记录笔尖历史轨迹 y_trace []; x_circle R * cos(linspace(0, 2*pi, 100)); % 大圆轮廓 y_circle R * sin(linspace(0, 2*pi, 100)); % 3. 创建图形窗口 fig figure(‘Position‘, [150, 150, 1000, 1000]); hold on; axis equal; axis([-R-1, R1, -R-1, R1]); grid on; title(‘万花尺动态仿真‘); % 4. 绘制静态元素 plot(x_circle, y_circle, ‘k--‘, ‘LineWidth‘, 0.5); % 大圆虚线 plot(0, 0, ‘k‘, ‘MarkerSize‘, 10, ‘LineWidth‘, 2); % 大圆圆心 % 5. 初始化动态图形对象 h_big_circle plot(NaN, NaN, ‘k-‘, ‘LineWidth‘, 1.5); % 小圆轮廓 h_center_line plot(NaN, NaN, ‘r-‘, ‘LineWidth‘, 1); % 小圆圆心到笔尖的连线 h_pen_point plot(NaN, NaN, ‘ro‘, ‘MarkerSize‘, 8, ‘MarkerFaceColor‘, ‘r‘); % 笔尖当前位置 h_trace plot(NaN, NaN, ‘b-‘, ‘LineWidth‘, 1.5); % 笔尖轨迹 % 6. 动画循环 for i 1:length(theta_anim) theta theta_anim(i); % 计算当前帧关键点坐标 Cx (R - r) * cos(theta); % 小圆圆心 Cy (R - r) * sin(theta); phi (R - r) / r * theta; % 小圆自转角度 Px Cx d * cos(phi); % 笔尖绝对坐标 Py Cy - d * sin(phi); % 注意这里是减号 % 计算小圆轮廓相对于固定坐标系 circle_theta linspace(0, 2*pi, 50); small_circle_x Cx r * cos(circle_theta); small_circle_y Cy r * sin(circle_theta); % 更新轨迹历史 x_trace [x_trace, Px]; y_trace [y_trace, Py]; % 更新图形对象数据 set(h_big_circle, ‘XData‘, small_circle_x, ‘YData‘, small_circle_y); set(h_center_line, ‘XData‘, [Cx, Px], ‘YData‘, [Cy, Py]); set(h_pen_point, ‘XData‘, Px, ‘YData‘, Py); set(h_trace, ‘XData‘, x_trace, ‘YData‘, y_trace); % 刷新图形 drawnow; % 控制动画速度可以保存为视频 % pause(0.05); end这段代码会生成一个动态过程你能看到小圆在大圆内滚动红色的笔尖划出蓝色的轨迹。通过调整num_frames和pause的时间可以控制动画的流畅度。实操心得在写动画循环时务必在循环开始前创建好图形对象plot返回的句柄然后在循环内只用set函数更新其‘XData‘和‘YData‘属性。这比在循环内反复调用plot要高效成百上千倍能保证动画流畅。另外使用axis equal和固定的坐标轴范围axis([xmin xmax ymin ymax])可以防止图形在动画过程中缩放和抖动。4. 参数探索与图案生成艺术万花尺的魅力在于其参数的微小变化能导致图案的巨变。我们可以系统地探索参数空间。4.1 关键参数的影响**半径比 ( R/r ) **这是决定图案“复杂度”和对称性的核心参数。设 ( k R/r )。**整数 ( k ) **当 ( k ) 为整数时( (R-r)/r k-1 ) 也是整数。小圆公转一圈自转 ( k-1 ) 圈笔尖轨迹是闭合的且有 ( k-1 ) 个尖点或花瓣。例如 ( R5, r1 ) (k5)会画出4个尖点的星形。**有理数 ( k p/q ) **其中 ( p, q ) 为互质整数。图案仍然是闭合的但需要小圆公转 ( q ) 圈才能完成一个完整周期图案通常有 ( p ) 个花瓣或循环。这是产生复杂而规律图案的主要区域。**无理数 ( k ) **图案永不重复最终会以某种密度填满大圆内的一个环状区域。**笔尖距离 ( d ) **控制图案的“风格”。( d r )笔尖在小圆内部。画出的曲线平滑没有交叉或尖点像光滑的波浪线环绕中心。( d r )笔尖恰在小圆周上。画出标准的内摆线可能有尖点。( d r )笔尖在小圆外部。曲线会自我交叉形成回环图案最为繁复华丽也是传统万花尺玩具最常见的模式。**初始相位 ( \delta ) **在自转角度 ( \phi ) 上加一个常数 ( \delta )相当于改变了笔尖在小圆上的起始位置。这会使整个图案旋转一个角度但不会改变其拓扑结构。4.2 编写参数化探索脚本我们可以写一个函数方便地生成和对比不同参数的图案。function draw_spirograph(R, r, d, n_cycles, color) % 绘制单个万花尺图案 % R: 大圆半径 % r: 小圆半径 % d: 笔尖距离 % n_cycles: 公转圈数 % color: 线条颜色 theta linspace(0, n_cycles*2*pi, 5000); % 核心方程 x (R - r) * cos(theta) d * cos((R - r)/r * theta); y (R - r) * sin(theta) - d * sin((R - r)/r * theta); plot(x, y, ‘Color‘, color, ‘LineWidth‘, 1.2); end % 使用示例对比不同d值的效果 figure(‘Position‘, [50, 50, 1200, 400]); subplot(1,3,1); draw_spirograph(5, 2, 1.0, 10, ‘b‘); % d r axis equal; grid on; title(‘d1.0 (d r)‘); subplot(1,3,2); draw_spirograph(5, 2, 2.0, 10, ‘r‘); % d r axis equal; grid on; title(‘d2.0 (d r)‘); subplot(1,3,3); draw_spirograph(5, 2, 3.0, 10, ‘g‘); % d r axis equal; grid on; title(‘d3.0 (d r)‘); sgtitle(‘笔尖距离d对万花尺图案的影响 (R5, r2)‘);运行这个脚本你可以直观地看到d参数如何从根本上改变曲线的形态。同样地你可以修改主脚本用循环来探索不同的 ( R/r ) 比值。注意事项当d r且(R-r)/r接近简单整数比时图案可能极其复杂需要足够多的采样点num_points设大比如10000以上和足够的公转圈数n_cycles设为分母的倍数才能画出完整、光滑的图案否则图形可能看起来是断开的或不够精细。这涉及到周期判断和采样率的问题在实际编码时需要注意。5. 进阶话题从旋轮线到异形齿轮万花尺的数学原理——圆内旋轮线在机械工程中有一个非常重要的应用异形齿轮非圆齿轮的齿廓设计。传统的等速传动齿轮是圆形的而非圆齿轮可以实现变速比传动让从动轴的速度按特定规律变化广泛应用于印刷机械、流量计、自动变焦机构等。5.1 基本原理关联想象一下如果把万花尺的大圆和小圆都变成齿轮那么它们的节曲线相当于齿轮的“分度圆”就不再是圆形。当一个小齿轮在一个固定的大齿轮内部啮合滚动时小齿轮上一个固定点的轨迹就是圆内旋轮线。如果把这个轨迹作为另一个齿轮的齿廓曲线那么这两个齿轮就能实现纯滚动的啮合传动。更具体地说一对共轭的异形齿轮其节曲线是一对相互滚动的封闭曲线。齿轮1上的某个齿廓相对于齿轮2的运动轨迹就是齿轮2上与之共轭的齿廓。这个数学关系与旋轮线生成过程同宗同源。对于圆内旋轮线情况对应的就是内啮合非圆齿轮。5.2 在Matlab中模拟异形齿轮啮合我们可以稍微拓展一下模型来模拟两个特定节曲线的齿轮啮合过程。这里以一个椭圆齿轮为例进行概念性演示。椭圆齿轮是最简单的非圆齿轮其节曲线是椭圆。% 椭圆齿轮啮合运动模拟概念演示 clear; clc; close all; % 定义椭圆参数作为齿轮1的节曲线 a 5; % 长半轴 b 3; % 短半轴 % 椭圆参数方程: x a*cos(t), y b*sin(t) % 模拟啮合假设齿轮2是与齿轮1共轭的另一个椭圆齿轮这里简化假设相同 % 核心是计算接触点的公法线必须通过瞬时旋转中心节点并满足传动比关系。 % 这里我们做一个简化可视化显示两个椭圆在纯滚动接触下的运动。 num_frames 100; t linspace(0, 2*pi, 200); % 齿轮1的椭圆 x1 a * cos(t); y1 b * sin(t); % 齿轮2的椭圆初始与齿轮1在长轴端点接触 % 假设中心距为两椭圆长轴之和外啮合 center_distance a a; % 简化假设齿轮2也是相同椭圆 offset_x center_distance; fig figure(‘Position‘, [100, 100, 900, 900]); hold on; axis equal; grid on; axis([-8, 12, -6, 6]); title(‘异形齿轮椭圆齿轮啮合运动概念演示‘); % 绘制静态的节曲线 plot(x1, y1, ‘b--‘, ‘LineWidth‘, 1); plot(x1offset_x, y1, ‘r--‘, ‘LineWidth‘, 1); h_gear1 plot(NaN, NaN, ‘b-‘, ‘LineWidth‘, 2); h_gear2 plot(NaN, NaN, ‘r-‘, ‘LineWidth‘, 2); h_contact_point plot(NaN, NaN, ‘go‘, ‘MarkerSize‘, 10, ‘MarkerFaceColor‘, ‘g‘); h_center_line plot(NaN, NaN, ‘k:‘, ‘LineWidth‘, 1); for i 1:num_frames % 齿轮1的旋转角度假设匀速转动 theta1 2*pi * (i-1)/num_frames; % 计算齿轮2的旋转角度 - 这是关键 % 对于椭圆齿轮转角关系不是线性的。需要根据椭圆弧长相等来求解。 % 这里进行大幅简化使用近似线性关系演示真实计算需数值求解积分。 theta2 - (a/b)^2 * theta1; % 近似传动比关系负号表示转向相反 % 旋转并绘制齿轮1 R1 [cos(theta1), -sin(theta1); sin(theta1), cos(theta1)]; gear1_rotated (R1 * [x1; y1])‘; set(h_gear1, ‘XData‘, gear1_rotated(:,1), ‘YData‘, gear1_rotated(:,2)); % 旋转、平移并绘制齿轮2 R2 [cos(theta2), -sin(theta2); sin(theta2), cos(theta2)]; gear2_rotated (R2 * [x1; y1])‘; gear2_rotated(:,1) gear2_rotated(:,1) center_distance; % 平移到右侧 set(h_gear2, ‘XData‘, gear2_rotated(:,1), ‘YData‘, gear2_rotated(:,2)); % 简化假设接触点在连心线上实际并非如此 % 实际接触点应由共轭齿廓决定这里用绿色圆点示意 contact_x a * cos(theta1); % 齿轮1上的点 contact_y b * sin(theta1); set(h_contact_point, ‘XData‘, contact_x, ‘YData‘, contact_y); % 绘制连心线 set(h_center_line, ‘XData‘, [0, center_distance], ‘YData‘, [0, 0]); drawnow; pause(0.05); end重要提示上面的代码是一个极度简化的概念演示。真实的异形齿轮设计涉及复杂的微分几何和共轭曲线理论节曲线设计首先根据所需的传动比函数设计两个齿轮的节曲线。齿廓生成根据包络原理或齿廓法线法由一条齿轮的齿廓求出另一条共轭齿廓。常用的齿廓有渐开线用于圆形齿轮或摆线齿廓。对于非圆齿轮齿廓通常需要数值方法求解。运动仿真需要精确计算两齿轮在任意转角下的接触点位置以验证啮合是否连续、平稳有无干涉。 真正的异形齿轮Matlab仿真是一个专业的课题需要用到符号数学、数值积分和微分方程求解。万花尺项目为我们理解其最基础的“滚动生成曲线”思想打开了一扇窗。6. 项目扩展与创意应用掌握了基础仿真后你可以尝试很多有趣的扩展把这个项目变成你的数字艺术工厂或数学探索工具。6.1 多级万花尺与复杂图案现实中万花尺玩具可以有多个齿轮嵌套。在数学上这相当于多个旋轮线运动的叠加。我们可以仿真一个齿轮在另一个齿轮内部滚动而笔尖又在第三个移动的齿轮上。% 二级万花尺仿真概念 R1 10; % 最外层固定圆半径 r1 3; % 第一级滚动圆半径 r2 1; % 第二级滚动圆半径在第一个滚动圆内部滚动 d 0.8 * r2; % 笔尖在第二级小圆上的距离 theta linspace(0, 50*pi, 20000); % 需要很多点因为运动更复杂 % 第一级运动圆心C1的轨迹 C1x (R1 - r1) * cos(theta); C1y (R1 - r1) * sin(theta); % 第一级小圆的自转角度 phi1 (R1 - r1) / r1 * theta; % 第二级运动是相对于第一级小圆的。 % 在第一级小圆的参考系中第二级小圆圆心C2‘的轨迹是圆内旋轮线。 % 然后再转换到固定坐标系。 % 这是一个复合变换方程会变长 x C1x (r1 - r2) * cos(phi1) d * cos( (r1 - r2)/r2 * phi1 phi1 ); y C1y (r1 - r2) * sin(phi1) - d * sin( (r1 - r2)/r2 * phi1 phi1 ); % 注意符号这里假设第二级也是内切滚动。 figure; plot(x, y, ‘b-‘, ‘LineWidth‘, 0.5); axis equal; title(‘二级万花尺图案‘);这种多级系统能产生极其复杂和精细的图案几乎像分形一样。6.2 彩色与三维化彩色轨迹根据笔尖的速度、曲率或者时间参数theta来给轨迹着色可以产生渐变色的艺术效果。使用scatter函数或者将轨迹分段用不同颜色绘制。% 使用色图根据参数theta着色 num_points length(x); colors jet(num_points); % jet色图 figure; hold on; axis equal; for i 1:num_points-1 plot(x(i:i1), y(i:i1), ‘-‘, ‘Color‘, colors(i,:), ‘LineWidth‘, 0.5); end三维万花尺将模型扩展到三维空间。例如让大圆和小圆不在同一个平面内或者笔尖在三维空间中运动。这需要引入第三个坐标z其方程可能是另一个频率的正弦或余弦函数叠加。z 0.5 * sin(5 * theta); % 一个简单的z轴振荡 plot3(x, y, z, ‘b-‘); grid on; axis equal; view(3);6.3 生成高分辨率图像与动画导出对于特别满意的图案你可能想保存为高清图片或视频。保存图片使用print或exportgraphics函数。exportgraphics(gcf, ‘my_spirograph.png‘, ‘Resolution‘, 300); % 保存为300DPI的PNG保存动画在动态仿真循环中使用getframe捕获帧然后用VideoWriter对象写入视频文件。v VideoWriter(‘spiro_animation.mp4‘, ‘MPEG-4‘); v.FrameRate 20; open(v); for i 1:num_frames % ... (更新图形的代码) frame getframe(fig); writeVideo(v, frame); end close(v);7. 常见问题与调试技巧在实际编写和运行仿真代码时你可能会遇到一些问题。这里记录一些典型的坑和解决方法。7.1 图案不闭合或看起来“没画完”问题描述曲线走到一半就断了或者看起来乱糟糟一团没有形成预期中对称、闭合的漂亮图案。原因分析theta的取值范围不够大如果(R-r)/r是一个有理数 ( p/q )最简分数那么图案的周期是 ( q ) 圈小圆公转圈数。如果你只仿真了不到 ( q ) 圈图案自然不完整。如果比值是无理数图案理论上永不重复你需要手动设定一个足够大的圈数来获得密集的填充效果。采样点num_points太少参数点太少Matlab用直线连接这些离散点时就显得很生硬尤其是曲线变化剧烈d r时的地方看起来就像断开了。数值精度问题在计算(R-r)/r * theta时如果R和r是浮点数计算机的浮点运算可能导致理论上有理数比值在数值上表现为无理数从而影响闭合性。解决方案对于有理数比值确保n_cycles即theta_end/(2*pi)是分母 ( q ) 的整数倍。可以通过计算lcm最小公倍数来智能确定。例如若(R-r)/r 3/7则周期为7圈。大幅增加num_points。对于复杂图案从5000起步必要时可增加到20000或更多。使用rational函数如果可用或手动将半径比设置为分数形式例如用R5; r2;而不是R5.0; r2.0;。但更实用的方法是接受近似闭合通过增加圈数和采样点来让“缺口”小到看不见。7.2 动画卡顿或不流畅问题描述动态仿真时画面一帧一帧地跳非常卡。原因分析在循环内创建新的图形对象每次循环都执行plot(x, y)会创建大量新的线条对象极度消耗内存和CPU。图形窗口渲染开销大如果图形元素太多比如轨迹点很多或者坐标轴范围在动态变化都会导致重绘变慢。没有使用drawnow limitratedrawnow会强制刷新所有待处理的事件有时过于频繁。解决方案绝对遵循“先创建后更新”的原则在for循环之前用plot(NaN, NaN)创建图形对象并保存句柄。在循环内只更新这些对象的XData和YData属性。固定坐标轴在循环前使用axis manual和axis([xmin xmax ymin ymax])锁定坐标轴范围。使用drawnow limitrate它只刷新图形一次比drawnow更高效。在动画循环中用drawnow limitrate替代drawnow。减少帧数如果不是必须减少num_frames。对于演示原理100-300帧通常足够平滑。预计算所有数据在循环外计算好所有帧的坐标数据循环内只进行数据赋值和刷新避免在循环内进行复杂计算。7.3 理解方程中的正负号问题描述按照公式写出的代码画出来的图形是镜像的或者方向不对。原因分析圆内旋轮线方程中y分量的负号- d * sin(...)至关重要。这个符号来源于坐标系定义y轴向上为正和小圆在内侧滚动时自转与公转角度的几何关系。如果错误地写成加号得到的就是“圆外旋轮线”外摆线在y轴镜像后的图形。解决方案记住这个经典形式。如果想探究原因可以自己推导一下在小圆滚动的瞬间建立向量关系图注意笔尖相对于小圆圆心位置的向量在转换到固定坐标系时其方向是公转角度和自转角度的差对于内摆线还是和对于外摆线。最稳妥的方法是以一组已知结果的参数例如R5, r2, d3进行测试与网络上或教科书上的标准图像对比来验证你的方程符号是否正确。7.4 处理大参数值下的数值溢出问题描述当theta值很大例如仿真很多圈或者(R-r)/r很大时cos和sin函数的参数可能变得非常大虽然三角函数本身值域有限但巨大的角度值在计算过程中可能引发精度问题或者导致图形渲染异常。解决方案对于闭合图案尽量只计算一个完整周期theta从0到2*pi*q。对于非闭合图案无理数比可以分段计算和绘图。另外确保你的theta向量是用linspace生成的而不是用累加的方式theta(i1) theta(i) delta后者可能因浮点误差导致终点不精确。