gradient_concept.py
1from manim import *2import numpy as np3 4config.tex_template = TexTemplateLibrary.ctex5config.tex_template.add_to_preamble(r"\setCJKmainfont{STSong}")6 7class GradientConcept(ThreeDScene):8 def create_derivative_value_tracker(self):9 """创建方向导数值的显示区域"""10 tracker = VGroup()11 # 将显示区域放在右侧12 tracker.arrange(DOWN, buff=0.1)13 tracker.to_edge(RIGHT, buff=0.5)14 return tracker15 16 def construct(self):17 # 创建标题18 title = Text(19 "方向导数与梯度",20 font="STSong",21 font_size=4822 )23 24 # 显示标题25 self.add_fixed_in_frame_mobjects(title)26 self.play(Write(title))27 self.wait(2)28 29 # 淡出标题30 self.play(FadeOut(title))31 32 # 创建3D坐标系33 self.axes = ThreeDAxes(34 x_range=[-1, 1, 0.5],35 y_range=[-1, 1, 0.5],36 z_range=[0, 2, 0.5],37 x_length=4, # 缩小坐标轴长度38 y_length=4,39 z_length=3,40 axis_config={"color": GREY, "include_ticks": False}41 )42 43 # 创建函数曲面 z = -x² - y² + 244 surface = Surface(45 lambda u, v: self.axes.c2p(u, v, -u**2 - v**2 + 2), # 降低最高点46 u_range=[-1, 1],47 v_range=[-1, 1],48 resolution=(30, 30),49 checkerboard_colors=[BLUE_D, BLUE_E],50 fill_opacity=0.651 )52 53 # 设置初始视角54 self.set_camera_orientation(phi=60*DEGREES, theta=-45*DEGREES)55 self.add(self.axes, surface)56 57 # 选择基准点 P(0.5,0.5,0)58 P = [0.5, 0.5, 0]59 P_surface = [0.5, 0.5, 1.5] # z = -0.5² - 0.5² + 2 = 1.560 point_P = Dot3D(self.axes.c2p(*P), color=RED, radius=0.05) # 缩小点的大小61 point_P_surface = Dot3D(self.axes.c2p(*P_surface), color=RED)62 63 # 显示基准点64 self.play(65 Create(point_P),66 Create(point_P_surface),67 Create(Line(68 self.axes.c2p(*P),69 self.axes.c2p(*P_surface),70 color=YELLOW71 ))72 )73 74 # 创建多个方向的切线75 angles = np.linspace(0, 2*np.pi, 8) # 8个不同方向76 directions = []77 tangent_lines = []78 direction_vectors = [] # 存储底面上的方向向量79 80 for angle in angles:81 # 计算方向向量82 direction = [np.cos(angle), np.sin(angle), 0]83 directions.append(direction)84 85 # 计算方向导数86 directional_derivative = -2*P[0]*direction[0] - 2*P[1]*direction[1]87 88 # 创建切线和方向向量89 tangent = self.create_tangent_line(90 P_surface,91 direction,92 directional_derivative,93 length=294 )95 direction_vector = Arrow3D(96 start=self.axes.c2p(*P),97 end=self.axes.c2p(*(np.array(P) + 0.3*np.array(direction))), # 缩放方向向量长度98 color=RED,99 thickness=0.03 # 稍细一些100 )101 tangent_lines.append(tangent)102 direction_vectors.append(direction_vector)103 104 # 逐个显示不同方向的切线和方向向量105 for tangent, vector in zip(tangent_lines, direction_vectors):106 self.play(107 Create(tangent),108 Create(vector),109 run_time=1110 )111 self.wait(0.5)112 113 # 创建说明文字114 explanation = Text(115 "不同方向的切线斜率表示\n沿不同方向的方向导数值",116 font="STSong",117 line_spacing=1.5118 ).scale(0.5)119 explanation.to_edge(RIGHT, buff=0.5).to_edge(UP, buff=1)120 self.add_fixed_in_frame_mobjects(explanation)121 self.play(Write(explanation))122 self.wait(1)123 124 # 创建方向导数计算公式125 formula = MathTex(126 "\\frac{\\partial f}{\\partial l} = ",127 "\\frac{\\partial f}{\\partial x}\\cos\\theta + ",128 "\\frac{\\partial f}{\\partial y}\\sin\\theta"129 ).scale(0.6)130 formula.to_edge(RIGHT, buff=0.5).shift(UP)131 self.add_fixed_in_frame_mobjects(formula)132 self.play(Write(formula))133 self.wait(1)134 135 # 计算梯度向量136 gradient = [2*P[0], 2*P[1], 0]137 gradient_arrow = Arrow3D(138 start=self.axes.c2p(*P),139 end=self.axes.c2p(*(np.array(P) + 0.5*np.array(gradient))),140 color=YELLOW,141 thickness=0.05142 )143 144 # 突出显示最大方向导数(梯度方向)145 self.play(146 Create(gradient_arrow),147 *[tangent.animate.set_opacity(0.3) for tangent in tangent_lines],148 *[vector.animate.set_opacity(0.3) for vector in direction_vectors],149 run_time=2150 )151 152 # 添加梯度标签153 gradient_label = MathTex("\\text{grad }f").next_to(gradient_arrow, RIGHT)154 self.play(Write(gradient_label))155 156 # 显示结论157 conclusion = VGroup(158 Text(159 "方向导数最大的方向是梯度的方向",160 font="STSong",161 font_size=24162 ),163 Text(164 "方向导数的最大值是梯度的模",165 font="STSong",166 font_size=24167 )168 ).arrange(DOWN, buff=0.3).to_edge(DOWN, buff=0.5)169 self.add_fixed_in_frame_mobjects(conclusion)170 self.play(Write(conclusion))171 self.wait(2)172 173 def create_tangent_line(self, point, direction, derivative, length=2):174 """创建切线"""175 direction = np.array(direction)176 direction = direction / np.linalg.norm(direction)177 178 # 计算曲面上的切向量179 # 对于f(x,y)=-x²-y²+2, 在点(x₀,y₀)处180 x0, y0 = point[0], point[1]181 dx = -2*x0 # ∂f/∂x = -2x182 dy = -2*y0 # ∂f/∂y = -2y183 184 # 计算方向导数185 dir_derivative = dx*direction[0] + dy*direction[1]186 187 # 构造切向量 (注意z分量是方向导数)188 tangent = np.array([189 direction[0],190 direction[1],191 dir_derivative192 ])193 # 归一化切向量194 tangent = tangent / np.linalg.norm(tangent)195 196 # 创建射线(只向一个方向延伸)197 end = np.array(point) + length * tangent198 199 return Line(200 self.axes.c2p(*point), # 从曲面上的点开始201 self.axes.c2p(*end),202 color=RED,203 stroke_width=3204 )205 206 def color_from_direction(self, direction, gradient):207 """根据方向与梯度的夹角确定颜色"""208 cos_theta = np.dot(direction[:2], gradient[:2]) / (209 np.linalg.norm(direction[:2])*np.linalg.norm(gradient[:2]))210 return interpolate_color(BLUE, RED, (cos_theta + 1)/2) 讲解
这个视频围绕「梯度和方向导数的关系」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「梯度和方向导数的关系」中的核心对象,尤其留意梯度、方向导数、等值面之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从梯度、方向导数、等值面、向量场这些概念进入,继续沿相邻问题观察同一类数学结构。