微元法可视化

微元法求曲线弧长

围绕微元法求曲线弧长,观察曲线弧长、微元法、定积分之间的关系与推理路径。

打开原视频

arc_length_microelement.py
1from manim import *2import numpy as np3 4class ArcLengthMicroelementScene(Scene):5    def construct(self):6        # 标题7        title = Text("微元法求曲线弧长", font_size=42)8        title.to_edge(UP)9        self.play(Write(title), run_time=1.5)10        self.wait(1)11        12        # 淡出标题13        self.play(FadeOut(title), run_time=1)14        15        # 介绍文本16        intro = Text("将曲线分割为无数个微小弧段,每段近似为直线", font_size=28)17        intro.to_edge(UP, buff=0.5)  # 调整位置,因为标题已淡出18        self.play(Write(intro), run_time=1.5)19        self.wait(1)20        21        # 淡出介绍22        self.play(FadeOut(intro), run_time=1)23        24        # 创建坐标系25        axes = Axes(26            x_range=[0, 4, 1],27            y_range=[0, 4, 1],28            x_length=10,29            y_length=7,30            axis_config={"color": BLUE}31        )32        33        # 添加坐标轴标签34        x_label = axes.get_x_axis_label("x")35        y_label = axes.get_y_axis_label("y")36        37        # 显示坐标系38        self.play(39            Create(axes),40            Write(x_label),41            Write(y_label),42            run_time=1.543        )44        45        # 定义函数 - 弯曲程度较大的复合函数46        def func(x):47            return 0.3 * x * x * x + 0.5 * np.sin(4 * x) + 148        49        def func_derivative(x):50            return 0.9 * x * x + 2 * np.cos(4 * x)51        52        # 创建函数曲线53        curve = axes.plot(func, x_range=[0, 2.5], color=RED, stroke_width=4)54        curve_label = MathTex("y = f(x)", font_size=28, color=RED)55        curve_label.next_to(curve.get_end(), UP + LEFT)56        57        self.play(58            Create(curve),59            Write(curve_label),60            run_time=261        )62        self.wait(1)63        64        # 定义弧长计算区间 - 选择弯曲明显的区域65        a, b = 1.0, 2.066        67        # 标记计算区间68        a_line = axes.get_vertical_line(axes.c2p(a, func(a)), color=GREEN)69        b_line = axes.get_vertical_line(axes.c2p(b, func(b)), color=GREEN)70        71        a_label = MathTex("a", font_size=28, color=GREEN)72        a_label.next_to(axes.c2p(a, 0), DOWN)73        74        b_label = MathTex("b", font_size=28, color=GREEN)75        b_label.next_to(axes.c2p(b, 0), DOWN)76        77        # 高亮弧长段78        arc_segment = axes.plot(func, x_range=[a, b], color=YELLOW, stroke_width=6)79        80        self.play(81            Create(a_line),82            Create(b_line),83            Write(a_label),84            Write(b_label),85            Create(arc_segment),86            run_time=287        )88        self.wait(1)89        90        # 弧长标记91        arc_label = Text("待求弧长", font_size=24, color=YELLOW)92        arc_label.move_to(axes.c2p(2, func(2) + 0.3))93        self.play(Write(arc_label), run_time=1)94        self.wait(1.5)95        96        # 开始演示分割过程97        self.play(FadeOut(arc_label), run_time=0.5)98        99        # 演示分割 - 选择8段作为例子100        n = 8101        dx_seg = (b - a) / n102        103        # 显示分割说明104        division_title = Text("将弧长分割成若干段", font_size=30, color=ORANGE)105        division_title.to_corner(UL).shift(DOWN * 0.5)106        self.play(Write(division_title), run_time=1)107        108        # 创建分割点和弧段109        division_points = []110        micro_elements = []111        colors = [YELLOW, BLUE, GREEN, PINK]112        113        # 第一步:显示分割点114        for i in range(n + 1):  # n+1个分割点115            x_point = a + i * dx_seg116            point = Dot(axes.c2p(x_point, func(x_point)), color=WHITE, radius=0.04)117            division_points.append(point)118        119        self.play(120            *[Create(point) for point in division_points],121            run_time=2122        )123        self.wait(1)124        125        # 第二步:显示分割后的弧段126        for i in range(n):127            x_left = a + i * dx_seg128            x_right = a + (i + 1) * dx_seg129            130            micro_arc = axes.plot(func, x_range=[x_left, x_right], 131                                color=colors[i % len(colors)], stroke_width=5)132            micro_elements.append(micro_arc)133        134        self.play(135            *[Create(elem) for elem in micro_elements],136            run_time=2137        )138        self.wait(1.5)139        140        # 淡出分割说明141        self.play(FadeOut(division_title), run_time=0.5)142        143        # 演示一小段的近似计算144        segment_index = 1  # 选择第2段进行演示(靠近a点)145        x_left = a + segment_index * dx_seg146        x_right = a + (segment_index + 1) * dx_seg147        148        # 高亮选中的段149        highlight_text = Text("选择其中一段进行近似计算", font_size=26, color=RED)150        highlight_text.to_corner(UL).shift(DOWN * 0.5)151        self.play(Write(highlight_text), run_time=1)152        153        # 高亮显示选中的弧段154        selected_arc = axes.plot(func, x_range=[x_left, x_right], 155                               color=RED, stroke_width=8)156        self.play(157            *[FadeOut(elem) for i, elem in enumerate(micro_elements) if i != segment_index],158            *[FadeOut(point) for i, point in enumerate(division_points) if i != segment_index and i != segment_index + 1],159            Transform(micro_elements[segment_index], selected_arc),160            run_time=2161        )162        self.wait(1)163        164        # 显示端点165        point1 = Dot(axes.c2p(x_left, func(x_left)), color=RED, radius=0.06)166        point2 = Dot(axes.c2p(x_right, func(x_right)), color=RED, radius=0.06)167        168        self.play(169            Create(point1),170            Create(point2),171            run_time=1172        )173        self.wait(0.5)174        175        # 放大演示:展示曲线弧和切线的关系176        self.play(FadeOut(highlight_text), run_time=0.5)177        178        zoom_text = Text("放大观察:曲线段越短,与切线段越接近", font_size=26, color=BLUE)179        zoom_text.to_corner(UL).shift(DOWN * 0.5)180        self.play(Write(zoom_text), run_time=1)181        182        # 创建放大的视图区域183        zoom_center_x = (x_left + x_right) / 2184        zoom_center_y = func(zoom_center_x)185        186        # 放大的坐标系187        zoom_range = 0.3188        zoom_axes = Axes(189            x_range=[zoom_center_x - zoom_range, zoom_center_x + zoom_range, 0.1],190            y_range=[zoom_center_y - zoom_range, zoom_center_y + zoom_range, 0.1],191            x_length=5.5,192            y_length=4.5,193            axis_config={"color": GRAY, "stroke_width": 1}194        )195        zoom_axes.move_to(RIGHT * 3 + UP * 0.2)196        197        # 放大视图的边框198        zoom_box = Rectangle(199            width=5.8, height=4.8,200            color=BLUE, stroke_width=3201        ).move_to(RIGHT * 3 + UP * 0.2)202        203        # 显示放大视图框架204        self.play(205            Create(zoom_box),206            Create(zoom_axes),207            run_time=1.5208        )209        210        # 演示不断缩短的过程211        segment_lengths = [0.28, 0.18, 0.11, 0.06]  # 从0.28开始,逐步缩短212        colors = [RED, ORANGE, YELLOW, GREEN]213        214        for i, length in enumerate(segment_lengths):215            # 当前段的范围216            current_left = zoom_center_x - length / 2217            current_right = zoom_center_x + length / 2218            219            # 显示当前阶段说明220            if i == 0:221                stage_text = Text("较长的曲线段", font_size=22, color=colors[i])222            elif i == 1:223                stage_text = Text("缩短曲线段", font_size=22, color=colors[i])224            elif i == 2:225                stage_text = Text("进一步缩短", font_size=22, color=colors[i])226            else:227                stage_text = Text("很短的曲线段", font_size=22, color=colors[i])228                229            stage_text.next_to(zoom_text, DOWN, aligned_edge=LEFT, buff=0.3)230            231            if i > 0:232                self.play(Transform(prev_stage_text, stage_text), run_time=0.8)233            else:234                self.play(Write(stage_text), run_time=0.8)235                prev_stage_text = stage_text236            237            # 放大视图中的曲线段238            zoom_curve = zoom_axes.plot(239                func, 240                x_range=[current_left, current_right], 241                color=colors[i], 242                stroke_width=8243            )244            245            # 曲线段的端点246            zoom_point1 = Dot(zoom_axes.c2p(current_left, func(current_left)), color=colors[i], radius=0.06)247            zoom_point2 = Dot(zoom_axes.c2p(current_right, func(current_right)), color=colors[i], radius=0.06)248            249            # 计算切线(在段中点处的切线)250            mid_x = zoom_center_x251            mid_y = func(mid_x)252            slope = func_derivative(mid_x)  # 在中点处的导数(切线斜率)253            254            # 切线段(从左端点到右端点)255            tangent_y_left = mid_y + slope * (current_left - mid_x)256            tangent_y_right = mid_y + slope * (current_right - mid_x)257            258            zoom_tangent = Line(259                zoom_axes.c2p(current_left, tangent_y_left),260                zoom_axes.c2p(current_right, tangent_y_right),261                color=PURPLE, stroke_width=6262            )263            264            # 显示曲线段和切线段265            if i == 0:266                self.play(267                    Create(zoom_curve),268                    Create(zoom_point1),269                    Create(zoom_point2),270                    run_time=1.5271                )272                self.wait(1)273                self.play(Create(zoom_tangent), run_time=1.2)274                self.wait(1.5)275            else:276                # 端点移动、曲线缩短和长曲线消失同时进行277                self.play(278                    ReplacementTransform(prev_curve, zoom_curve),279                    ReplacementTransform(prev_point1, zoom_point1),280                    ReplacementTransform(prev_point2, zoom_point2),281                    ReplacementTransform(prev_tangent, zoom_tangent),282                    run_time=3.0  # 稍长的时间让变化过程更清晰自然283                )284                self.wait(1.5)285            286            # 保存当前对象以便下次更新287            prev_curve = zoom_curve288            prev_point1 = zoom_point1289            prev_point2 = zoom_point2290            prev_tangent = zoom_tangent291        292        # 最终说明293        self.play(FadeOut(zoom_text), run_time=0.5)294        final_zoom_text = Text("极限情况:曲线段与切线段完全重合", font_size=24, color=GREEN)295        final_zoom_text.to_corner(UL).shift(DOWN * 0.5)296        self.play(Write(final_zoom_text), run_time=1.5)297        self.wait(2)298        299        # 回到主视图进行详细分析300        self.play(301            FadeOut(final_zoom_text),302            FadeOut(prev_stage_text),303            *[FadeOut(mob) for mob in [zoom_box, zoom_axes, prev_curve, 304                                      prev_point1, prev_point2, prev_tangent]],305            run_time=1.5306        )307        308        # 添加原来的直线近似到主视图309        approx_line = Line(310            axes.c2p(x_left, func(x_left)),311            axes.c2p(x_right, func(x_right)),312            color=PURPLE, stroke_width=6313        )314        315        approximation_text = Text("用直线段近似这段弧长", font_size=26, color=PURPLE)316        approximation_text.to_corner(UL).shift(DOWN * 0.5)317        self.play(Write(approximation_text), run_time=1)318        319        self.play(Create(approx_line), run_time=1.5)320        self.wait(1)321        322        # 构造直角三角形进行详细分析323        dy = func(x_right) - func(x_left)324        325        # 水平线 (dx)326        horizontal_line = Line(327            axes.c2p(x_left, func(x_left)),328            axes.c2p(x_right, func(x_left)),329            color=BLUE, stroke_width=4330        )331        332        # 竖直线 (dy)333        vertical_line = Line(334            axes.c2p(x_right, func(x_left)),335            axes.c2p(x_right, func(x_right)),336            color=GREEN, stroke_width=4337        )338        339        # 标签340        dx_label = MathTex("dx", font_size=28, color=BLUE)341        dx_label.next_to(horizontal_line, DOWN, buff=0.2)342        343        dy_label = MathTex("dy", font_size=28, color=GREEN)344        dy_label.next_to(vertical_line, RIGHT, buff=0.2)345        346        ds_label = MathTex("ds", font_size=28, color=PURPLE)347        ds_label.next_to(approx_line.get_center(), UP + LEFT, buff=0.2)348        349        self.play(FadeOut(approximation_text), run_time=0.5)350        351        triangle_text = Text("构造直角三角形分析", font_size=26, color=WHITE)352        triangle_text.to_corner(UL).shift(DOWN * 0.5)353        self.play(Write(triangle_text), run_time=1)354        355        self.play(356            Create(horizontal_line),357            Create(vertical_line),358            Write(dx_label),359            Write(dy_label),360            Write(ds_label),361            run_time=2362        )363        self.wait(2)364        365        # 显示计算公式366        self.play(FadeOut(triangle_text), run_time=0.5)367        368        # 显示弧长微元公式(右侧)369        formula_group = VGroup()370        371        # 勾股定理372        pythagorean = MathTex(r"ds^2 = dx^2 + dy^2", font_size=24)373        pythagorean.to_corner(UR).shift(LEFT * 1.5 + DOWN * 0.5)374        formula_group.add(pythagorean)375        376        # 弧长微元公式377        ds_formula = MathTex(r"ds = \sqrt{dx^2 + dy^2}", font_size=24)378        ds_formula.next_to(pythagorean, DOWN, aligned_edge=LEFT, buff=0.2)379        formula_group.add(ds_formula)380        381        # 导数形式382        derivative_form = MathTex(r"ds = \sqrt{1 + \left(\frac{dy}{dx}\right)^2} dx", font_size=24)383        derivative_form.next_to(ds_formula, DOWN, aligned_edge=LEFT, buff=0.2)384        formula_group.add(derivative_form)385        386        self.play(Write(pythagorean), run_time=1)387        self.wait(0.5)388        self.play(Write(ds_formula), run_time=1)389        self.wait(0.5)390        self.play(Write(derivative_form), run_time=1)391        self.wait(2)392        393        # 清除详细分析,准备回到整体演示394        self.play(395            *[FadeOut(mob) for mob in [396                micro_elements[segment_index], point1, point2, approx_line,397                horizontal_line, vertical_line, dx_label, dy_label, ds_label,398                formula_group399            ]],400            run_time=1.5401        )402        403        # 恢复完整的弧长段用于后续演示404        complete_arc = axes.plot(func, x_range=[a, b], color=YELLOW, stroke_width=6)405        self.play(Create(complete_arc), run_time=1)406        self.wait(0.5)407        408        # 显示极限和积分概念409        limit_text = Text("当段长 → 0 时,所有直线段长度之和趋向于曲线弧长", font_size=24)410        limit_text.to_edge(DOWN)411        self.play(Write(limit_text), run_time=2)412        self.wait(1.5)413        414        # 最终的积分公式415        final_formula = MathTex(r"L = \int_a^b \sqrt{1 + \left(\frac{dy}{dx}\right)^2} dx", 416                              font_size=36, color=GOLD)417        final_box = SurroundingRectangle(final_formula, buff=0.3, color=GOLD)418        final_group = VGroup(final_formula, final_box)419        final_group.move_to(RIGHT * 4 + UP * 2.5)  # 右移到更右侧位置,避免重叠420        421        self.play(422            FadeOut(limit_text),423            Write(final_group),424            run_time=2425        )426        427        self.wait(2)428        429        # 结论430        conclusion = Text("微元法:将复杂的曲线问题转化为简单的直线问题", font_size=26)431        conclusion.to_edge(DOWN)432        self.play(Write(conclusion), run_time=2)433        434        self.wait(3)435        436        # 淡出所有元素437        self.play(*[FadeOut(mob) for mob in self.mobjects], run_time=1.5)438 439 440if __name__ == "__main__":441    # 直接渲染场景442    scene = ArcLengthMicroelementScene()443    scene.render()

讲解

这个视频围绕「微元法求曲线弧长」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。

开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。

画面中出现的文字「微元法求曲线弧长」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。

随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。

核心公式可以写成:

y=f(x)y = f(x)

这类公式可以和画面中的符号一一对应。

观察路径

观察路径可以分三步:先锁定「微元法求曲线弧长」中的核心对象,尤其留意曲线弧长、微元法、定积分之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。

本页可以从曲线弧长、微元法、定积分、黎曼和极限这些概念进入,继续沿相邻问题观察同一类数学结构。