Vibe Coding范式中的ComfyUI节点式工作流。你将学习ComfyUI的节点图设计、自定义节点开发、工作流复用、模型管理、与A1111对比,以及如何利用ComfyUI构建灵活的AI图像生成Pipeline,适合AI图像创作者学习。

岗位关联

需要学习ComfyUI节点式工作流,理解AI技术栈才能有效评估技术能力。本篇涵盖LangChain/RAG/Agent、API集成、向量数据库,帮助你构建AI应用系统能力。

目录
  1. 可视化演示
  2. ComfyUI节点图架构与工作流设计
  3. 自定义节点开发与扩展机制
  4. 工作流复用与模板管理
  5. 模型管理与LoRA加载策略
  6. ComfyUI与A1111对比分析
  7. AI 应用场景
  8. Python 演示
  9. 常见问答
  10. 常见误区
  11. 参考来源

可视化演示

展示ComfyUI节点图工作流架构、LoRA模型管理与显存策略、以及与A1111界面哲学对比。切换模式探索节点式AI图像生成的不同维度。

本节配有交互动效(进入视口后自动加载,约 3KB)。

ComfyUI节点图工作流架构 模型加载→条件编码→采样去噪→VAE解码→图像输出,每步骤独立节点通过连线传递数据 节点图架构实现模块化:可任意插入ControlNet、LoRA、后处理节点,灵活组合Pipeline workflow = node(type, params) → edge(data_flow) 节点图设计是ComfyUI的核心优势

ComfyUI节点图架构与工作流设计

ComfyUI是一款基于节点图的可视化AI工作流编辑器,其核心设计理念是将复杂的AI图像生成过程拆解为多个独立的功能节点。每个节点负责一个特定的处理步骤,例如模型加载、条件编码、采样去噪或图像解码。用户通过在画布上拖拽节点并用连线建立数据通道,即可构建出完整的图像生成管线。这种节点式界面使得工作流的每个环节都清晰可见,极大地提升了复杂任务的可调试性和可复现性。

在ComfyUI的节点体系中,三种核心节点类型构成了图像生成的基本骨架。CheckpointLoaderSimple节点负责加载Stable Diffusion模型的三个关键组件:主模型MODEL用于去噪预测、CLIP文本编码器用于将提示词转化为条件向量、VAE变分自编码器用于潜在空间与像素空间的转换。KSampler节点接收模型和条件信息,在潜在空间中执行迭代去噪采样过程。VAEDecode节点则将采样得到的潜在表示解码为可视的像素图像。

ComfyUI的底层数据结构采用有向无环图即DAG来组织节点之间的执行关系。在DAG中,数据只能沿连线的单一方向流动,从上游节点的输出端口传递到下游节点的输入端口,不存在循环依赖。系统通过拓扑排序算法确定节点的执行顺序,确保每个节点在被执行时其所有上游依赖已经完成计算。当工作流中存在菱形依赖等复杂拓扑时,DAG结构能够正确处理并保证执行结果的一致性。

DAG结构的一个重要优势是支持选择性执行和增量更新。当用户修改工作流中的某个节点参数时,ComfyUI可以智能判断哪些下游节点需要重新执行,而不必重跑整个工作流。此外,没有依赖关系的节点理论上可以并行执行,进一步提升系统性能。这种基于DAG的执行模型是ComfyUI处理复杂多步骤AI工作流的核心技术基础。

自定义节点开发与扩展机制

ComfyUI提供了强大的自定义节点扩展机制,允许开发者使用Python编写新的功能节点来扩展系统能力。每个自定义节点需要继承Node基类,并实现三个核心的类方法。INPUT_TYPES方法返回一个字典,包含required和optional两个键,分别定义必填和可选的输入参数及其类型。RETURN_TYPES方法返回一个元组,声明该节点各输出端口的数据类型。FUNCTION方法指定节点执行时实际调用的方法名称。

节点注册是自定义节点接入ComfyUI框架的关键步骤。开发者需要在自定义节点包的init.py文件中定义两个全局字典:NODE_CLASS_MAPPINGS将字符串名称映射到节点类对象,NODE_DISPLAY_NAME_MAPPINGS将同一名称映射到人类可读的显示名称。ComfyUI在启动时会自动扫描custom_nodes目录下的所有子目录,加载其中的Python模块并读取这两个映射字典,完成节点的自动注册和界面集成。

第三方节点包的管理遵循一套约定优于配置的原则。标准的自定义节点包是一个包含init.py文件的Python包目录,放置在ComfyUI根目录的custom_nodes文件夹下。系统启动时会自动扫描并导入这些包。社区中流行的第三方节点包如ComfyUI-Manager提供了图形化的安装和管理界面,支持一键安装、更新和禁用节点包。用户也可以通过git clone方式手动将第三方包下载到custom_nodes目录完成安装。

自定义节点的开发实践中,类型安全和错误处理是两个重要考量。INPUT_TYPES中定义的类型标签如MODEL、IMAGE、LATENT等不仅用于界面连线的颜色区分,还在运行时用于验证连接的合法性。开发者应当在节点方法中妥善处理异常输入,返回有意义的错误信息。此外,通过定义IS_CHANGED方法可以实现节点的动态更新检测,INPUT_IS_LIST和OUTPUT_IS_CLASS等标志则提供了更灵活的数据处理方式。

工作流复用与模板管理

ComfyUI支持将完整的工作流导出为JSON格式文件,该文件记录了工作流中所有节点的类型、参数配置、位置信息以及节点之间的连接关系。用户可以通过菜单将当前工作流保存为JSON文件,也可以从文件中导入恢复完整的工作流状态。这一机制使得工作流的分享、版本管理和团队协作变得非常便捷。在ComfyUI社区中,工作流JSON文件是最常见的知识分享形式之一。

模板化管理是提升工作流复用效率的重要手段。用户可以基于常用的工作流模式创建参数化模板,将模型名称、分辨率、采样步数等关键参数抽象为可配置变量。通过修改模板参数即可快速生成适配不同需求的变体工作流,避免从零开始搭建。部分高级用户还利用ComfyUI的API接口,通过程序化方式动态修改工作流JSON中的节点参数,实现批量生成任务的自动化。

ComfyUI社区围绕工作流模板形成了活跃的生态系统。OpenArt、Civitai等社区平台上有大量用户分享的现成工作流模板,涵盖文生图、图生图、超分辨率、风格迁移等多种应用场景。ComfyUI-Manager插件提供了在线浏览和安装社区工作流的功能。此外,HuggingFace等模型托管平台也为ComfyUI工作流提供了版本管理和分享基础设施,进一步促进了最佳实践的传播和复用。

模型管理与LoRA加载策略

在实际AI图像生成项目中,往往需要同时使用多个模型文件来协同工作。一个典型的SDXL工作流可能需要主模型文件约6.5GB显存、VAE模型约300MB、以及多个LoRA微调模型各约50到200MB。ComfyUI采用按需加载和自动卸载的显存管理策略,只在节点执行时加载所需模型到GPU显存,执行完毕后将不再使用的模型释放或移至CPU内存缓存,从而在有限显存条件下支持多模型的协同工作。

LoRA即Low-Rank Adaptation是一种轻量化的模型微调技术,通过低秩矩阵分解来近似全参数微调的效果。在ComfyUI中,LoraLoader节点接收主模型和CLIP作为输入,将LoRA权重融合到对应组件后输出修改后的模型。用户可以串联多个LoraLoader节点实现LoRA堆叠,同时叠加多种风格或概念。每个LoRA节点有model_strength和clip_strength两个权重参数,用于控制该LoRA对模型和文本编码的影响强度。

LoRA堆叠时需要注意权重分配的合理性。当多个LoRA的强度总和过高时,可能导致生成图像出现伪影或色彩溢出。实践中通常将各LoRA权重控制在0.3到0.8之间,并根据实际效果逐步微调。此外,LoRA的加载顺序也会影响最终效果,因为后加载的LoRA是在前面LoRA已经修改过的模型基础上再次融合。用户应当通过实验确定最佳的LoRA组合和加载顺序。

ControlNet是另一种重要的条件控制机制,它通过复制UNet的编码器子网络创建条件分支,将空间控制信号如边缘图、深度图、人体姿态等注入到生成过程中。在ComfyUI中,ApplyControlNet节点接收预处理后的条件图像和正条件编码,输出融合空间约束的新条件编码。ControlNet可以与LoRA在同一工作流中并行使用,实现多条件联合控制,例如同时使用Canny边缘控制和LoRA风格化来生成精确可控的图像。

ComfyUI与A1111对比分析

ComfyUI和Stable Diffusion WebUI即A1111是目前最流行的两个Stable Diffusion前端工具,它们采用了截然不同的界面设计哲学。ComfyUI使用节点图界面,用户通过连接不同功能节点来构建工作流,每个数据流转步骤都清晰可见。A1111则采用传统的表单式界面,用户在固定布局的页面中填写参数并点击生成。两种界面使用完全相同的底层模型和算法,在相同参数下生成的图像质量没有本质差异。

在复杂工作流处理能力上,ComfyUI的节点图架构具有明显优势。当需要组合多个模型、添加条件控制、实现多步后处理时,节点图可以直观地展示整个处理流程的逻辑关系。而A1111的表单界面在面对复杂需求时需要依赖扩展脚本,各步骤之间的数据流转不够透明。但在简单的单图生成场景中,A1111的表单界面更加简洁直接,用户无需理解节点连接的概念即可快速上手。

在API集成和批处理方面,ComfyUI同样展现出更好的灵活性。ComfyUI的工作流JSON可以直接作为API请求体发送给后端服务,天然支持程序化调用。用户可以编写脚本动态生成工作流JSON,实现大规模批量图像生成。A1111虽然也提供了REST API,但需要将表单参数逐一映射为API字段,在复杂工作流场景下的集成复杂度更高。

综合来看,ComfyUI更适合需要多模型组合、复杂条件控制、批量处理管线的高级应用场景,以及需要精确控制每个生成步骤的专业用户。A1111则更适合快速原型验证、简单图像生成和初学者入门使用。两者并非替代关系,而是面向不同使用场景和不同技术水平用户的互补工具。随着AI应用开发的深入,越来越多的专业开发者选择ComfyUI作为其核心工作流引擎。

AI 应用场景

节点式AI图像生成Pipeline构建

阶段:推理优化

解决的问题:在AI图像生成应用中,需要构建包含SDXL主模型、LoRA风格微调和ControlNet空间控制的复合工作流,实现精确可控的高质量图像生成。传统表单式工具难以灵活组合多种条件控制,而ComfyUI的节点图架构允许开发者将模型加载、文本编码、LoRA融合、ControlNet条件注入、采样去噪和VAE解码等步骤以DAG方式组织,形成可复用、可调试的完整生成管线。

具体案例:某电商公司需要批量生成商品展示图,要求同时应用品牌风格LoRA和构图ControlNet。开发者在ComfyUI中构建节点图:CheckpointLoaderSimple加载SDXL模型,两个LoraLoader分别叠加品牌风格和产品类别LoRA,ApplyControlNet注入产品轮廓边缘图作为空间约束,KSampler执行去噪采样,VAEDecode解码输出,最终通过PreviewImage和SaveImage节点完成预览和保存。整个工作流导出为JSON模板,通过API接口实现自动化批量生成。

自定义节点的批量图像处理扩展

阶段:推理优化

解决的问题:在AI图像处理管线中,标准节点库无法满足特定的业务需求,如自定义超分辨率算法、批量水印添加、特定格式转换等。ComfyUI的自定义节点扩展机制允许开发者使用Python编写专用处理节点,通过INPUT_TYPES和RETURN_TYPES定义接口规范,无缝集成到现有工作流中,实现业务逻辑的模块化和可复用。

具体案例:某内容平台需要对AI生成的图像进行后处理,包括超分辨率放大和水印添加。开发者编写两个自定义ComfyUI节点:UpscaleNode实现基于双三次插值的2倍超分辨率放大,通过INPUT_TYPES接收IMAGE类型输入,RETURN_TYPES声明IMAGE类型输出;WatermarkNode在图像指定位置叠加半透明品牌水印。两个节点注册到NODE_CLASS_MAPPINGS后,即可在ComfyUI工作流中与标准节点自由组合,构建从模型推理到后处理的完整自动化管线。

Python 演示

python263 行
"""ComfyUI节点式工作流核心逻辑模拟"""from typing import Any  class Node:    """节点基类,模拟ComfyUI中所有节点的抽象父类"""     CATEGORY = "default"     def __init__(self):        self._id = None        self._name = None     @classmethod    def INPUT_TYPES(cls):        """定义节点输入参数类型,返回包含required和optional的字典"""        return {"required": {}}     @classmethod    def RETURN_TYPES(cls):        """定义节点返回值类型元组"""        return ()     @classmethod    def FUNCTION(cls):        """指定节点执行时调用的方法名"""        return "execute"     def execute(self, **kwargs):        raise NotImplementedError  class CheckpointLoaderSimple(Node):    """加载SD模型检查点,返回模型、CLIP和VAE三个组件"""     CATEGORY = "loaders"     @classmethod    def INPUT_TYPES(cls):        return {"required": {"ckpt_name": ("STRING",)}}     @classmethod    def RETURN_TYPES(cls):        return ("MODEL", "CLIP", "VAE")     @classmethod    def FUNCTION(cls):        return "load_checkpoint"     def load_checkpoint(self, ckpt_name):        return (f"model:{ckpt_name}", f"clip:{ckpt_name}", f"vae:{ckpt_name}")  class KSampler(Node):    """K采样器节点,使用指定算法在潜在空间中进行去噪采样"""     CATEGORY = "sampling"     @classmethod    def INPUT_TYPES(cls):        return {"required": {            "model": ("MODEL",),            "seed": ("INT",),            "steps": ("INT",),            "cfg": ("FLOAT",),            "sampler_name": ("STRING",),            "positive": ("CONDITIONING",),            "negative": ("CONDITIONING",),            "latent_image": ("LATENT",),        }}     @classmethod    def RETURN_TYPES(cls):        return ("LATENT",)     @classmethod    def FUNCTION(cls):        return "sample"     def sample(self, model, seed, steps, cfg, sampler_name,               positive, negative, latent_image):        return ({"samples": f"latent_{seed}_{steps}steps"},)  class VAEDecode(Node):    """VAE解码器,将潜在空间数据解码为像素空间图像"""     CATEGORY = "latent"     @classmethod    def INPUT_TYPES(cls):        return {"required": {            "samples": ("LATENT",),            "vae": ("VAE",),        }}     @classmethod    def RETURN_TYPES(cls):        return ("IMAGE",)     @classmethod    def FUNCTION(cls):        return "decode"     def decode(self, samples, vae):        return ("decoded_image",)  class PreviewImage(Node):    """预览图像节点,将图像输出到用户界面"""     CATEGORY = "image"     @classmethod    def INPUT_TYPES(cls):        return {"required": {"images": ("IMAGE",)}}     @classmethod    def RETURN_TYPES(cls):        return ()     def execute(self, images):        return ()  class ExecutionEngine:    """执行引擎,负责构建DAG图并按拓扑序执行节点"""     def __init__(self):        self.nodes = {}        self.connections = []     def add_node(self, node_id, node_instance):        node_instance._id = node_id        self.nodes[node_id] = node_instance     def add_connection(self, from_id, from_slot, to_id, to_slot):        self.connections.append({            "from": from_id, "from_slot": from_slot,            "to": to_id, "to_slot": to_slot        })     def get_execution_order(self):        in_degree = {nid: 0 for nid in self.nodes}        adj = {nid: [] for nid in self.nodes}        for conn in self.connections:            adj[conn["from"]].append(conn["to"])            in_degree[conn["to"]] += 1        queue = [nid for nid in self.nodes if in_degree[nid] == 0]        order = []        while queue:            nid = queue.pop(0)            order.append(nid)            for nxt in adj[nid]:                in_degree[nxt] -= 1                if in_degree[nxt] == 0:                    queue.append(nxt)        return order     def _get_input_names(self, node):        """获取节点输入参数名列表(按槽位顺序)"""        names = []        input_types = node.INPUT_TYPES()        for name in input_types.get("required", {}):            names.append(name)        return names     def _get_defaults(self, node):        """为未连接的输入参数生成默认值"""        defaults = {}        input_types = node.INPUT_TYPES()        for name, spec in input_types.get("required", {}).items():            type_name = spec[0] if isinstance(spec, tuple) else spec            if type_name == "STRING":                defaults[name] = "default_model"            elif type_name == "INT":                defaults[name] = 42            elif type_name == "FLOAT":                defaults[name] = 7.5            elif type_name == "CONDITIONING":                defaults[name] = "empty_cond"            elif type_name == "LATENT":                defaults[name] = {"samples": "empty_latent"}        return defaults     def execute(self):        order = self.get_execution_order()        outputs = {}        for nid in order:            node = self.nodes[nid]            inputs = self._get_defaults(node)            param_names = self._get_input_names(node)            for conn in self.connections:                if conn["to"] == nid:                    src_out = outputs[conn["from"]]                    slot_name = param_names[conn["to_slot"]]                    inputs[slot_name] = src_out[conn["from_slot"]]            func_name = node.FUNCTION()            method = getattr(node, func_name)            result = method(**inputs)            outputs[nid] = result        return outputs  if __name__ == "__main__":    print("=== ComfyUI节点式工作流模拟 ===\n")     engine = ExecutionEngine()     loader = CheckpointLoaderSimple()    engine.add_node("1", loader)     sampler = KSampler()    engine.add_node("2", sampler)     decoder = VAEDecode()    engine.add_node("3", decoder)     preview = PreviewImage()    engine.add_node("4", preview)     engine.add_connection("1", 0, "2", 0)    engine.add_connection("1", 2, "3", 1)    engine.add_connection("2", 0, "3", 0)    engine.add_connection("3", 0, "4", 0)     print("执行顺序:", engine.get_execution_order())    results = engine.execute()    print("节点1输出:", results["1"])    print("节点2输出:", results["2"])    print("节点3输出:", results["3"])     print("\n=== 自定义节点: 超分辨率 ===\n")     class UpscaleNode(Node):        """自定义超分辨率节点,将图像分辨率提升2倍"""        CATEGORY = "custom"         @classmethod        def INPUT_TYPES(cls):            return {"required": {"image": ("IMAGE",)}}         @classmethod        def RETURN_TYPES(cls):            return ("IMAGE",)         def execute(self, image):            return (f"upscaled_2x({image})",)     engine2 = ExecutionEngine()    engine2.add_node("1", CheckpointLoaderSimple())    engine2.add_node("2", KSampler())    engine2.add_node("3", VAEDecode())    engine2.add_node("4", UpscaleNode())     engine2.add_connection("1", 0, "2", 0)    engine2.add_connection("1", 2, "3", 1)    engine2.add_connection("2", 0, "3", 0)    engine2.add_connection("3", 0, "4", 0)     results2 = engine2.execute()    print("超分输出:", results2["4"])    print("执行路径: 1 -> 2 -> 3 -> 4")
目的:模拟ComfyUI节点式工作流系统的核心逻辑,包括节点基类定义、检查点加载器、K采样器、VAE解码器等核心节点实现、基于DAG拓扑排序的执行引擎,以及自定义超分辨率节点的扩展示例
  1. class Node:定义节点抽象基类,所有ComfyUI节点都继承此类,提供INPUT_TYPES、RETURN_TYPES、FUNCTION三个核心类方法接口
  2. def INPUT_TYPES(cls):类方法INPUT_TYPES定义节点的输入参数规范,返回包含required和optional的字典,用于界面渲染和类型校验
  3. def RETURN_TYPES(cls):类方法RETURN_TYPES声明节点输出端口的数据类型元组,如("MODEL", "CLIP", "VAE")表示三个输出
  4. class CheckpointLoaderSimple(Node):检查点加载节点,继承Node基类,负责加载SD模型文件并输出MODEL、CLIP、VAE三个组件供下游节点使用
  5. return ("MODEL", "CLIP", "VAE")声明该节点有三个输出端口,分别返回主模型、文本编码器和VAE解码器,对应SD模型的三大核心组件
  6. class KSampler(Node):K采样器节点,是图像生成管线的核心,接收模型、条件编码和潜在图像,执行迭代去噪采样过程
  7. class ExecutionEngine:执行引擎类,负责管理节点图DAG结构,实现拓扑排序确定执行顺序,并按序驱动各节点执行
  8. def get_execution_order(self):基于Kahn算法实现拓扑排序,计算每个节点的入度,从入度为零的节点开始逐步确定执行顺序
  9. queue = [nid for nid in self.nodes if in_degree[nid] == 0]初始化队列,将所有入度为零即无上游依赖的节点加入,这些节点可以最先执行
  10. def execute(self):工作流执行主方法,按拓扑序遍历每个节点,收集输入、调用执行方法、存储输出供下游节点使用
  11. slot_name = param_names[conn["to_slot"]]将连接的槽位索引映射为参数名称,使上游输出能正确传入下游节点方法的对应参数
  12. class UpscaleNode(Node):自定义超分辨率节点示例,继承Node基类,演示如何通过INPUT_TYPES和RETURN_TYPES定义接口并实现自定义图像处理逻辑

常见问答

ComfyUI的执行引擎如何确定节点的执行顺序?
ComfyUI的执行引擎使用基于Kahn算法的拓扑排序来确定节点执行顺序。首先计算每个节点的入度(即有多少条连线指向该节点),将入度为零的节点(没有上游依赖)加入执行队列。然后依次从队列中取出节点执行,并将其下游节点的入度减一。当某个下游节点的入度变为零时,说明其所有上游依赖已完成,将其加入队列。这一过程持续到所有节点都被执行完毕。对于菱形依赖等复杂拓扑,拓扑排序能够保证正确的线性执行顺序。
ComfyUI中自定义节点的INPUT_TYPES和RETURN_TYPES分别起什么作用?
INPUT_TYPES是一个类方法,返回一个包含required和optional两个键的字典,定义节点的所有输入参数。每个参数包含类型标签如STRING、INT、MODEL、IMAGE等,用于界面渲染输入控件、验证连线合法性以及运行时参数传递。RETURN_TYPES也是一个类方法,返回一个类型字符串元组,声明节点输出端口的数量和类型。例如CheckpointLoaderSimple返回("MODEL", "CLIP", "VAE")表示它有三个输出端口。这两个方法共同构成了节点的接口契约,是ComfyUI节点系统的核心设计模式。
ComfyUI和A1111生成的图像质量有差异吗?为什么?
在相同模型、相同参数设置下,ComfyUI和A1111生成的图像质量没有差异。这是因为两者使用完全相同的底层Stable Diffusion模型和采样算法。图像生成质量取决于模型权重、VAE解码器、采样步数、CFG值等参数,而非前端工具的界面形式。ComfyUI的节点图界面和A1111的表单界面只是参数配置和工作流组织方式的不同,最终调用的核心计算代码是一致的。所谓ComfyUI效果更好是一种常见误区,实际差异可能来自参数设置的不同或后处理步骤的差异。

常见误区

误区ComfyUI的节点图界面生成的图像比A1111质量更好

正解ComfyUI和A1111使用完全相同的底层Stable Diffusion模型和采样算法,在相同参数下生成质量没有差异。节点图界面只是提供了更灵活的工作流组织方式,并不影响模型推理的数学计算过程。

为何常见:用户从A1111切换到ComfyUI后,由于工作流中可能隐含了不同的默认参数、后处理步骤或模型配置,导致生成效果发生变化,容易误认为是工具本身的差异。

误区ComfyUI中每个节点都需要手动连接,搭建工作流非常繁琐

正解ComfyUI支持多种方式简化工作流搭建:可以导入社区分享的JSON模板一键恢复完整工作流;通过ComfyUI-Manager安装预置节点包获取常用组合;通过API接口程序化生成工作流无需手动连线。

为何常见:初学者首次接触节点图界面时,面对空白画布需要逐个添加和连接节点,容易产生畏难情绪,不了解模板和API等自动化手段的存在。

误区ComfyUI只能用于Stable Diffusion图像生成

正解ComfyUI是一个通用的节点式AI工作流引擎,除了SD图像生成外,还支持视频生成如AnimateDiff、音频处理如音频生成和转换、3D模型生成和处理、图像分割、人脸检测等多种AI任务。社区已开发了覆盖多种模态的自定义节点包。

为何常见:ComfyUI最初因Stable Diffusion而流行,大多数入门教程也聚焦于图像生成场景,导致用户形成ComfyUI仅用于SD图像生成的刻板印象。

参考来源

  • webpageComfyUI官方GitHub仓库ComfyUI项目的官方代码仓库,包含核心源码、架构文档、节点API说明和安装指南,是了解ComfyUI节点系统实现的最权威来源。
  • webpageComfyUI自定义节点开发文档官方提供的自定义节点开发指南,详细说明INPUT_TYPES、RETURN_TYPES、FUNCTION等核心接口的定义规范和NODE_CLASS_MAPPINGS注册机制。
  • paperAdding ControlNet to Stable Diffusion论文ControlNet的原论文,详细阐述了通过零卷积层将条件控制信号注入预训练扩散模型的方法,是理解ComfyUI中ControlNet节点工作原理的理论基础。