ComfyUI AnimateDiff 新手教程:最小文生视频工作流搭建与原理解析
一、前言
刚接触 ComfyUI 和 AnimateDiff 时,最常见的两个问题通常是:
- 工作流到底能不能先跑起来?
- 节点为什么要这样连接,能不能换一种接法?
很多教程一上来就堆很多插件和节点,结果还没跑通就先被劝退了。
这篇文章只做一件事:
用一套最小可运行的 AnimateDiff 工作流,先跑通,再理解原理,最后明确下一步该怎么扩展。
这篇文章适合:
- 刚接触 ComfyUI 的新手
- 已经装好了 AnimateDiff 插件,但还不理解节点逻辑的人
- 想做教程、想把原理讲清楚的人
工作流截图

输出图片截图

输出视频截图

输出没做任何优化,所以效果肯定不怎么滴,先能用就行,要啥自行车
二、先看最终目标
这套最小工作流的目标非常明确:
- 输入一段 prompt
- 生成一组连续帧
- 再把这些帧合成为 mp4 视频
当前这套最小工作流里,关键参数是:
- 分辨率:
512 x 512 - 帧数:
16 - AnimateDiff motion module:
mm_sd_v15_v2.ckpt - 输出视频节点:
VHS_VideoCombine - 输出帧率:
8 fps
也就是说,这套工作流的最终结果不是"一张图",而是:
一个 16 帧、约 2 秒左右的视频片段。
因为:
- 16 帧
- 8 fps
所以视频时长大约是:
16 / 8 = 2 秒
这非常适合作为新手入门练习。
三、先跑通
这一部分不讲太深的原理,目标只有一个:
把 AnimateDiff 最小工作流跑起来。
1. 需要哪些节点?
这套最小工作流主要由下面这些节点组成:
CheckpointLoaderSimpleCLIPTextEncode(正向)CLIPTextEncode(负向)EmptyLatentImageADE_AnimateDiffLoaderGen1ADE_UseEvolvedSamplingKSamplerVAEDecodeSaveImageVHS_VideoCombine
其中,真正和 AnimateDiff 直接相关的核心节点其实只有两个:
ADE_AnimateDiffLoaderGen1ADE_UseEvolvedSampling
2. 关键模型文件
至少需要两类模型:
底模 checkpoint
例如:
dreamshaper_8.safetensors
AnimateDiff motion module
当前工作流里使用的是:
mm_sd_v15_v2.ckpt
这个 motion module 是 AnimateDiff 的关键,没有它就不是 AnimateDiff 视频链路。
3. 最小工作流连接顺序
这套工作流的主链路可以概括成:
text
CheckpointLoaderSimple
├─ CLIP → 正向/负向 CLIPTextEncode
├─ VAE → VAEDecode
└─ MODEL → ADE_AnimateDiffLoaderGen1
ADE_AnimateDiffLoaderGen1
└─ MODEL → ADE_UseEvolvedSampling
ADE_UseEvolvedSampling
└─ MODEL → KSampler
EmptyLatentImage
└─ LATENT → KSampler
正向/负向 CLIPTextEncode
└─ CONDITIONING → KSampler
KSampler
└─ LATENT → VAEDecode
VAEDecode
├─ IMAGE → SaveImage
└─ IMAGE → VHS_VideoCombine
如果只想先抄作业,这一段最重要。
4. 推荐起步参数
为了保证新手更容易跑通,建议先不要改太多参数。
EmptyLatentImage
- width:
512 - height:
512 - batch size:
16
ADE_AnimateDiffLoaderGen1
- model_name:
mm_sd_v15_v2.ckpt - beta_schedule:
autoselect
KSampler
- steps:
24 - cfg:
7 - sampler:
euler - scheduler:
normal - denoise:
1.0
VHS_VideoCombine
- fps:
8 - 输出格式:
video/h264-mp4
5. 正负 prompt 示例
正向 prompt
text
a young woman standing in the wind, cinematic lighting, detailed face, natural motion, slight hair movement
负向 prompt
text
blurry, low quality, bad anatomy, deformed, distorted face, extra fingers, jitter, flicker
这里负向词里已经专门加入了:
jitterflicker
因为视频和单图不一样,视频特别怕抖动和闪烁。
6. 跑通后的正确结果是什么?
如果工作流已经跑通,应该能得到两类输出:
1)图片序列
SaveImage 会保存一组逐帧图片,文件名前缀可以是:
animatediff_frames
2)mp4 视频
VHS_VideoCombine 会输出一个 h264 编码的 mp4 视频,并接收 IMAGE 作为输入,另外还预留了 audio 输入位
这套工作流不是只出图,而是同时保留逐帧图片和最终视频。
这点非常重要,后面解释原理时就会明白为什么这样设计。
四、为什么这些节点要这样连接?
跑通之后,才是最关键的部分:
为什么这套工作流不能乱连?
这一部分按"节点职责"的方式来讲,而不是一上来就讲很抽象的理论。对新手来说,这样更容易消化。
1. CheckpointLoaderSimple:整条链路的起点
这个节点会输出三样东西:
MODELCLIPVAE
这三个输出分别负责不同的事。
MODEL
它是基础扩散模型,相当于整条链路里的"主画师"。后面无论是单图还是视频采样,真正负责画画的底层能力都来自这个模型。
CLIP
它负责理解 prompt,把文字变成模型可以利用的条件信息。所以它必须连接到正向和负向 CLIPTextEncode。
VAE
它负责把 latent 解码成真正可见的图片。所以它必须连接到 VAEDecode,而 VAEDecode 的输入也明确包括 samples 和 vae 。
CheckpointLoaderSimple 不是"加载一个模型就完了",而是同时提供了图像生成链路的三大核心部件。
2. CLIPTextEncode:为什么要分正向和负向?
这是 Stable Diffusion 工作流的标准做法,但在视频里意义更大。
正向 prompt
告诉模型:
- 应该生成什么内容
- 画面的大方向是什么
- 希望出现什么动作和氛围
负向 prompt
告诉模型:
- 什么是不要的
- 哪些错误特征要尽量避免
在当前工作流里,负向词包含:
blurrydeformedjitterflicker
这说明负向 prompt 在视频里不仅是"修图",更是在控制:
帧与帧之间的稳定性。
也就是说,视频里不仅怕"画坏",还怕"跳帧感太强"。
3. EmptyLatentImage:为什么 batch size = 16 很关键?
这个节点很容易被低估。
当前参数是:
- 512
- 512
- 16
在普通图像工作流里,batch size 往往只是"一次生成多少张图"。但在 AnimateDiff 里,它的意义更接近:
一次生成多少帧。
也就是说,这里的 16 不只是"并行生成 16 张图",而是:
生成一个长度为 16 帧的时间序列。
这是理解 AnimateDiff 的一个关键点:
- 单图工作流处理的是一个 latent
- AnimateDiff 工作流处理的是一组带时间关系的 latent
所以这个节点不是单纯在"准备画布",而是在准备:
视频的 latent 帧容器。
4. ADE_AnimateDiffLoaderGen1:为什么要接在 MODEL 后面?
这个节点是 AnimateDiff 链路的第一核心节点。
当前工作流里它使用的 motion module 是:
mm_sd_v15_v2.ckpt
这个节点输入的是:
MODEL
输出的也是:
MODEL
很多新手第一次会觉得奇怪:
为什么输入输出都是 MODEL?
原因在于:
这个节点不是单纯加载文件,而是在"给基础模型增加时序运动能力"。
也就是说:
- 基础 checkpoint 决定"画什么"
- motion module 决定"怎么动"
ADE_AnimateDiffLoaderGen1 的作用,就是把这两者结合起来。
所以它必须接在 CheckpointLoaderSimple.MODEL 后面,原因非常简单:
因为 AnimateDiff 不是替代底模,而是增强底模。
没有底模,它无从附着;没有 motion module,底模又只能出静态图。
5. ADE_UseEvolvedSampling:为什么还要再经过一次这个节点?
这一步是很多新手最不理解的地方。
既然 ADE_AnimateDiffLoaderGen1 已经把 motion module 挂到底模上了,为什么不直接接 KSampler?
因为:
"模型具备运动能力" 和 "采样时正确启用时序规则" 是两回事。
可以把它理解成:
ADE_AnimateDiffLoaderGen1:改造模型ADE_UseEvolvedSampling:改造采样方式
当前工作流里,ADE_UseEvolvedSampling:
- 接收
model m_models留空- 输出
MODEL
它的作用是:
让后面的采样器知道:现在处理的不是普通单图采样,而是一组带时间关联的帧。
如果少了这一步,后面的 KSampler 仍然可能按普通单图批处理的方式去理解这些 latent,时序一致性就会失去意义。
所以这条链:
text
Checkpoint MODEL
→ ADE_AnimateDiffLoaderGen1
→ ADE_UseEvolvedSampling
→ KSampler
本质上做了两件事:
- 给模型挂上运动模块
- 让采样器按 AnimateDiff 的逻辑工作
这就是为什么这一步不能省。
6. KSampler:为什么它仍然是核心?
虽然加入了 AnimateDiff,但真正执行扩散去噪的依然是 KSampler。
它接收四类信息:
- model
- positive conditioning
- negative conditioning
- latent image
这说明 AnimateDiff 并没有另起炉灶,而是:
在 Stable Diffusion 原有采样体系上,增加了一层时间建模能力。
所以视频不是最后"拼接"出来的,而是在 KSampler 这一步就已经按视频帧序列的方式被生成了。
这一点很重要。
VHS_VideoCombine 只是把图片打包成视频。
真正决定"动起来像不像视频"的,是前面的采样过程,而不是最后的视频节点。
7. VAEDecode:为什么不能直接输出图片?
因为 KSampler 输出的是 latent,不是普通图片。
所以必须经过:
VAEDecode.samples <- KSampler.LATENTVAEDecode.vae <- CheckpointLoaderSimple.VAE
VAEDecode 的作用就是:
把模型内部的 latent 表示,解码成可以看到的真实图像。
这一步完成后,才真正得到了逐帧图片。
8. 为什么 VAEDecode 后面同时接 SaveImage 和 VHS_VideoCombine?
这是一个非常值得新手学习的工作流设计习惯。
SaveImage
作用是保留逐帧结果。这样可以:
- 逐帧看哪里开始跳
- 分析哪一帧出现大变化
- 为后期修帧、补帧做准备
当前 JSON 里,SaveImage 的前缀就是 animatediff_frames 。
VHS_VideoCombine
作用是把图像序列按固定帧率合成为 mp4。当前工作流里,它接收 images 和可选 audio 输入,且帧率参数为 8 。
这说明它做的是:
封装,而不是生成运动。
所以这两个节点不是重复,而是分工明确:
SaveImage负责调试和检查VHS_VideoCombine负责最终交付
五、最后扩展:跑通之后该怎么继续学?
当这套最小工作流已经跑通并且基本理解之后,下一步就不是继续乱加节点,而是要有顺序地扩展。
1. 先把"视频稳定性"学明白
虽然这套工作流已经能出视频,但很可能会出现:
- 每帧构图差异较大
- 镜头远近变化明显
- 服装变化
- 背景漂移
- 角色一致性不够
这不是操作错误,而是因为:
纯文本驱动的最小工作流,本来就只适合学习原理,不适合直接追求高一致性成片。
所以第一步要学的是:
- 如何写更稳定的 prompt
- 如何减少镜头跳变
- 如何让动作幅度更小
2. 从文生视频过渡到图生视频
如果发现当前视频"每帧差异太大",这其实非常正常。
因为纯文生相当于每一帧都在"重新想象同一个主题"。更稳定的路线通常是:
先固定一张起始图,再让它轻微动起来。
这就是图生 AnimateDiff。它比纯文本 AnimateDiff 更适合做:
- 角色微动作
- 头发摆动
- 呼吸感
- 轻微镜头推进
对教程型学习来说,这也是最自然的下一步。
3. 再引入更强的一致性约束
当最小工作流已经理解清楚之后,后面可以继续加:
- ControlNet:控制动作结构
- IPAdapter:控制人物风格和一致性
- FaceID / InstantID:锁人物身份
- LoRA:锁角色设定
- 视频后期工具:补帧、超分、剪辑、调色
但一定要注意顺序:
先搞懂 AnimateDiff 本身,再叠加一致性工具。
否则节点一多,反而不知道到底是谁在起作用。
六、新手最容易误解的 5 个点
这一节建议保留,因为对新手读者帮助很大。
误解 1:batch size = 一次多出几张图
不完全对。
在当前 AnimateDiff 工作流里,EmptyLatentImage 的 16 更接近:
16 帧视频片段。
误解 2:VHS_VideoCombine 决定视频动不动
不对。
VHS_VideoCombine 只是把图片按 8 fps 合成 mp4 。真正决定视频是否有连续动作感的是:
- AnimateDiff motion module
- Evolved Sampling
- KSampler 的多帧联合采样
误解 3:AnimateDiff 只是换了个 checkpoint
不对。
当前工作流里真正承担运动建模的是:
mm_sd_v15_v2.ckpt这样的 motion module
它不是普通底模,而是时间维度上的能力模块。
误解 4:能生成视频就代表视频稳定
不对。
"能跑通"和"能稳定出片"是两回事。最小工作流适合学习,不代表已经具备高一致性成片能力。
误解 5:节点能接上就说明逻辑对
也不对。
AnimateDiff 里很多节点虽然名字接近,但输入输出类型不一样。所以一定要理解:
- 这个节点是在处理模型
- 还是在处理采样
- 还是在处理输出
七、常见问题与排查
1. 为什么只出图片,没有视频?
大概率是:
- 没有加
VHS_VideoCombine - 或者视频节点没有正确接到
VAEDecode.IMAGE
正确做法是:
VAEDecode.IMAGE -> VHS_VideoCombine.images
2. 为什么能出视频,但看起来像一堆跳变很大的图片?
这是纯文本最小工作流最常见的问题。原因通常是:
- prompt 约束不够强
- 没有参考图锚定
- 没有角色一致性控制
- 没有结构控制
这不是 AnimateDiff 完全失效,而是:
已经进入了"能跑通,但还不稳定"的阶段。
3. 为什么 mm_sd_v15_v2.ckpt 找不到?
通常是:
- motion module 没放对目录
- 放好了但没重启 ComfyUI
当前工作流里,ADE_AnimateDiffLoaderGen1 使用的就是:
mm_sd_v15_v2.ckpt
4. 为什么会感觉每帧变化太大?
这其实是很多新手都会遇到的正常现象。
因为视频看起来"像动作",前提通常是:
相邻帧变化小,但方向连续。
如果每一帧都像重新生成了一张新照片,那看起来就不是"动起来",而是"跳来跳去"。
这也是为什么最小工作流适合学习原理,但后面一定要继续学图生视频和一致性控制。
八、这套最小工作流为什么适合做教程?
主要有三个原因。
1. 节点数量少,学习成本低
虽然有 AnimateDiff,但核心新增节点其实只有:
ADE_AnimateDiffLoaderGen1ADE_UseEvolvedSampling
这对新手非常友好。
2. 能完整覆盖一条视频生成链路
它包含了:
- 模型加载
- 文本条件
- 多帧 latent
- AnimateDiff 注入
- 时序采样
- 解码
- 图片输出
- 视频输出
也就是说,虽然是最小工作流,但已经是一条完整闭环。
3. 很适合作为后续进阶的基础
后面要加:
- 图生输入
- ControlNet
- IPAdapter
- 一致性模块
都可以在这套链路上逐步升级,而不是从头推倒重来。
九、总结
这篇文章的核心可以概括为:
AnimateDiff 的视频生成,不是"先生成很多图,再拼起来",而是在采样阶段就把一组 latent 当作连续帧来联合生成。
这也是为什么当前工作流必须这样连接:
CheckpointLoaderSimple提供MODEL / CLIP / VAECLIPTextEncode提供正负条件EmptyLatentImage提供16帧的 latent 容器ADE_AnimateDiffLoaderGen1把mm_sd_v15_v2.ckpt挂到底模上ADE_UseEvolvedSampling让采样器按时序规则工作,并接收model输入KSampler负责真正的多帧联合采样VAEDecode把 latent 变成图像SaveImage保留逐帧结果,前缀可设为animatediff_framesVHS_VideoCombine以8 fps合成为 mp4,并接收images输入
对新手来说,更合适的学习顺序是:
先跑通、再理解、最后扩展。
这比一开始就装很多插件、追求复杂成片,更容易真正学会 AnimateDiff。