多元微积分可视化

方向导数的几何意义

围绕方向导数的几何意义,观察方向导数、偏导数、切平面之间的关系与推理路径。

打开原视频

directional_derivative.py
1from manim import *2import numpy as np3 4config.tex_template = TexTemplateLibrary.ctex5config.tex_template.add_to_preamble(r"\setCJKmainfont{STSong}")6 7class DirectionalDerivative(ThreeDScene):8    def create_vertical_plane(self, direction_vector, base_point):9        """创建过方向向量且垂直于底面的平面"""10        u_vec = np.array(direction_vector) / np.linalg.norm(direction_vector)11        base = np.array(base_point)12        13        return Surface(14            lambda u, v: [15                base[0] + u * u_vec[0],16                base[1] + u * u_vec[1],17                base[2] + v18            ],19            u_range=[-4, 4],20            v_range=[-4, 4],21            resolution=(1, 1),22            color=RED,23            fill_opacity=0.3,24            stroke_opacity=025        )26 27    def create_secant(self, p1, p2):28        """创建曲面上的割线"""29        t_values = np.linspace(-3, 3, 400)30        points = []31        for t in t_values:32            x = p1[0] + t*(p2[0]-p1[0])33            y = p1[1] + t*(p2[1]-p1[1])34            if abs(x) > 3 or abs(y) > 3:35                continue36            z = x**2 + y**237            points.append(self.axes.c2p(x, y, z))38        return VMobject().set_points_smoothly(points).set_style(39            stroke_color=YELLOW,40            stroke_width=8,41            stroke_opacity=142        )43 44    def create_tangent_line(self, point, direction, length=6):45        """创建直线切线"""46        direction = np.array(direction) / np.linalg.norm(direction)47        p = np.array(point)48        49        z_change = 2 * (p[0]*direction[0] + p[1]*direction[1])50        tangent = np.array([direction[0], direction[1], z_change])51        tangent = tangent / np.linalg.norm(tangent)52        53        start = p - length * tangent54        end = p + length * tangent55        56        return Line(57            self.axes.c2p(*start),58            self.axes.c2p(*end),59            color=WHITE,60            stroke_width=661        )62 63    def construct(self):64        # 创建3D坐标系65        self.axes = ThreeDAxes(66            x_range=[-2, 2, 1],67            y_range=[-2, 2, 1],68            z_range=[0, 8, 2],69            x_length=6,70            y_length=6,71            z_length=4,72            axis_config={"color": GREY, "include_ticks": False}73        ).add_coordinates()74 75        # 创建标题76        title = Text("方向导数的几何意义", font="STSong")77        title.scale(0.6).to_corner(UP+LEFT)78        self.add_fixed_in_frame_mobjects(title)79        self.play(Write(title))80        self.wait(1)81 82        # 创建函数曲面83        surface = Surface(84            lambda u, v: self.axes.c2p(u, v, u**2 + v**2),85            u_range=[-2, 2], v_range=[-2, 2],86            resolution=(30, 30),87            checkerboard_colors=[BLUE_D, BLUE_E],88            fill_opacity=0.889        )90 91        # 设置固定视角92        self.set_camera_orientation(phi=60*DEGREES, theta=-40*DEGREES)93        self.stop_ambient_camera_rotation()94 95        # 显示坐标系和曲面96        self.play(Create(self.axes), Create(surface))97        self.wait(1)98 99        # 创建底面上的点P(1,1)100        P = (1, 1, 0)101        P_surface = (1, 1, 2)102        point_P = Dot3D(self.axes.c2p(*P), color=RED)103        point_P_surface = Dot3D(self.axes.c2p(*P_surface), color=RED)104        vertical_line = Line(self.axes.c2p(*P), self.axes.c2p(*P_surface), color=YELLOW)105 106        # 创建曲面方程和基准点标记107        surface_equation = MathTex(108            "z = f(x,y)",109            color=WHITE110        ).to_corner(UP+RIGHT)111        self.add_fixed_in_frame_mobjects(surface_equation)112        self.play(Write(surface_equation))113 114        # 创建基准点标记115        P0_label = MathTex(116            "P_0(x_0,y_0)",117            color=RED118        ).scale(0.6).next_to(119            self.axes.c2p(*P),  # 使用三维坐标定位120            UR,  # 改为右上方向121            buff=0.2122        ).shift(LEFT*0.3 + DOWN*2)  # 微调位置123        self.add_fixed_in_frame_mobjects(P0_label)124 125        # 显示基准点和标记126        self.play(Create(point_P))127        self.wait(0.5)128 129        # 创建方向向量130        angle = 30*DEGREES131        u_vec = [np.cos(angle), np.sin(angle), 0]132        vector_scale = 0.8133        direction_arrow = Arrow3D(134            start=self.axes.c2p(*P),135            end=self.axes.c2p(*(np.array(P) + vector_scale * np.array(u_vec))),136            color=GREEN137        )138 139        # 显示方向向量140        self.play(Create(direction_arrow))141        self.wait(0.1)  # 确保箭头已渲染142 143        # 修改方向向量标记的位置144        # 将标签直接附加在箭头末端145        direction_label = MathTex(146            "\\vec{l}",147            color=GREEN148        ).scale(0.7).next_to(149            direction_arrow.get_end(),  # 使用箭头末端坐标150            UR,  # 右上方向151            buff=0.1152        ).shift(LEFT*0.2 + DOWN*2)  # 微调位置153 154        # 确保标签位于底面155        direction_label.move_to(156            self.axes.c2p(157                *(np.array(P) + vector_scale * np.array(u_vec))[:2],  # 取x,y坐标158                0  # z坐标强制为0159            )160        ).shift(RIGHT*0.3 + DOWN*2)  # 最终微调161 162        # 确保标记固定在底面163        self.add_fixed_in_frame_mobjects(direction_label)164        self.play(Write(direction_label))165        self.wait(1)166 167        # 计算Ph点坐标(使用第一个h值)168        h = 0.8  # 使用固定值169        Ph = np.array(P) + h * vector_scale * np.array(u_vec)170        Ph_clipped = np.clip(Ph[:2], -1.8, 1.8)171        Ph_surface = (*Ph_clipped, Ph_clipped[0]**2 + Ph_clipped[1]**2)172 173        # 创建几何元素174        points = {175            "P": P,176            "Ph": (*Ph_clipped, 0),177            "P_surface": P_surface,178            "Ph_surface": Ph_surface179        }180 181        # 创建点和线182        ph_point = Dot3D(183            self.axes.c2p(*points["Ph"]), 184            color=PINK,  # 改为更醒目的颜色185            radius=0.08  # 稍微加大点的大小186        )187        ph_surface_point = Dot3D(self.axes.c2p(*points["Ph_surface"]), color=GREEN)188        ph_line = Line(189            self.axes.c2p(*points["Ph"]), 190            self.axes.c2p(*points["Ph_surface"]), 191            color=YELLOW192        )193 194        # 创建移动点标记195        Ph_label = MathTex(196            "P(x_0+\\rho\\cos\\alpha, y_0+\\rho\\sin\\alpha)",197            color=PINK198        ).scale(0.6).next_to(199            self.axes.c2p(*points["Ph"]),  # 使用三维坐标定位200            UR,  # 保持右上方向201            buff=0.2202        ).shift(LEFT*0.5+DOWN*2)  # 向左微调避免超出屏幕203        self.add_fixed_in_frame_mobjects(Ph_label)204 205        # 显示移动点和标记206        self.play(Create(ph_point))207        self.wait(0.5)208 209        # 3. 显示曲面上对应的点和垂线210        self.play(211            Create(point_P_surface),212            Create(ph_surface_point),213            Create(vertical_line),214            Create(ph_line),215            run_time=1216        )217        self.wait(1)218 219        # 4. 显示垂直平面220        vertical_plane = self.create_vertical_plane(u_vec, P)221        self.play(Create(vertical_plane))222        self.wait(0.5)223 224        # 5. 显示交线225        intersection_curve = self.create_secant(226            (P[0] - 3*u_vec[0], P[1] - 3*u_vec[1], 0),227            (P[0] + 3*u_vec[0], P[1] + 3*u_vec[1], 0)228        ).set_color(YELLOW_A)  # 使用更亮的黄色229        self.play(Create(intersection_curve))230        self.wait(0.1)231 232        # 创建rho标记233        t = 0.5  # 中点参数234        mid_x = (1 - t)*P[0] + t*points["Ph"][0]235        mid_y = (1 - t)*P[1] + t*points["Ph"][1]236        mid_z = 0237        rho_mid = self.axes.c2p(mid_x, mid_y, mid_z)238 239        rho_label = MathTex(240            "\\rho",241            color=YELLOW242        ).scale(0.8).next_to(243            rho_mid,244            RIGHT*0.5+ DOWN*0.5, 245            buff=0.2246        )247        self.add_fixed_in_frame_mobjects(rho_label)248 249        # 创建曲面上的连线250        surface_line = Line(251            self.axes.c2p(*P_surface),252            self.axes.c2p(*points["Ph_surface"]),253            color=BLUE_C,254            stroke_width=4255        )256 257        # 调整动画顺序258        # 6. 显示rho标记和曲面连线259        self.play(Write(rho_label))260        self.play(Create(surface_line))261        self.wait(1)262 263        # 7. 显示切线264        final_tangent = self.create_tangent_line(265            P_surface,266            u_vec,267            length=6268        )269        self.play(Create(final_tangent))270        self.wait(1)271 272        # 修改旋转方向273        current_theta = self.camera.theta274        self.move_camera(275            theta=current_theta + 90 * DEGREES,  # 改为顺时针旋转276            run_time=2277        )278        self.wait(1)279 280        # 创建方向导数定义281        definition = VGroup(282            MathTex("\\text{方向导数:}"),283            MathTex(284                "f'_l(x_0,y_0) = \\lim_{\\rho \\to 0} \\frac{f(x_0+\\rho\\cos\\alpha, y_0+\\rho\\sin\\alpha) - f(x_0,y_0)}{\\rho}"285            )286        ).arrange(RIGHT, buff=0.5)287        definition.scale(0.8).to_edge(DOWN)288        self.add_fixed_in_frame_mobjects(definition)289 290        # 显示定义291        self.play(Write(definition))292        self.wait(1)293 294        # 添加结论说明295        conclusion = Text(296            "函数f在P₀点沿方向l的方向导数为图中白色切线的斜率",297            font="STSong",298            font_size=24  # 缩小字体大小299        ).scale(0.8).next_to(definition, DOWN, buff=0.2)  # 添加缩放和调整间距300        self.add_fixed_in_frame_mobjects(conclusion)301        self.play(Write(conclusion))302        self.wait(2)303 304def main():305    import os306    os.system("manim -pqh directional_derivative.py DirectionalDerivative")307 308if __name__ == "__main__":309    main()

讲解

这个视频围绕「方向导数的几何意义」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。

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

画面中出现的文字「方向导数的几何意义」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。

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

观察路径

观察路径可以分三步:先锁定「方向导数的几何意义」中的核心对象,尤其留意方向导数、偏导数、切平面之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。

本页可以从方向导数、偏导数、切平面、函数图像这些概念进入,继续沿相邻问题观察同一类数学结构。