在基于 Stable Diffusion WebUI 的自动化出图流程里,通过 HTTP 接口驱动 ControlNet 是最常用的做法。但不少人在联调时发现,接口明明返回了正常状态码,生成的图片却完全没有受到姿态、边缘或深度图的约束。这种“调用了但没效果”的现象,通常不是模型坏了,而是请求数据没有真正命中 ControlNet 的解析逻辑。本文从接口契约和模型匹配两个层面,拆解这类问题的根因与排查路径。

Payload 结构为何必须严格嵌套
WebUI 的 ControlNet 扩展并不读取根级的自定义字段,它只在 alwayson_scripts 这个固定对象里寻找 controlnet 单元。很多初学者把控制参数直接放在请求体顶层,例如写 controlnet_model 或 input_image,服务端收到后根本不会进入插件处理逻辑,自然没有任何约束生效。正确的做法是将所有 ControlNet 配置塞进 alwayson_scripts.controlnet.args 数组中,每一个数组元素代表一个控制单元。
一个最小可用的控制单元需要包含 input_image、module、model、weight 等键。其中 input_image 必须是 base64 编码且带 data URI 前缀的字符串,module 指定预处理器如 openpose、canny,model 则是后端加载的模型名。如果漏掉 input_image,扩展会认为该单元未启用;若 module 与 model 类型不匹配,例如用 canny 预处理器配 depth 模型,也会静默失效。下面是一段典型的错误与正确对照。
// 错误:参数散落在顶层,ControlNet 忽略
{
"prompt": "a cat",
"controlnet_model": "control_v11p_sd15_openpose",
"input_image": "data:image/png;base64,xxxx"
}
// 正确:嵌套在 alwayson_scripts
{
"prompt": "a cat",
"alwayson_scripts": {
"controlnet": {
"args": [
{
"input_image": "data:image/png;base64,xxxx",
"module": "openpose",
"model": "control_v11p_sd15_openpose",
"weight": 1.0,
"resize_mode": "Just Resize",
"enabled": true
}
]
}
}
}
从维护角度看,建议把 alwayson_scripts 的组装逻辑封装成独立函数,避免业务代码里到处拼接 JSON。同时开启 WebUI 的 --api 后,访问 /docs 能直接看到 OpenAPI 定义,对照字段层级写代码比看社区片段更可靠。当出图无约束时,第一步应打印最终发出的 JSON,确认结构是否真的嵌对了位置。
模型名称拼写怎样精确匹配才不空载
ControlNet 后端通过文件名精确匹配模型,不存在模糊搜索。你在 model 字段填的字符,必须和 extensions/sd-webui-controlnet/models 目录下的 safetensors 文件去掉扩展名后的名称完全一致。常见失误包括把 control_v11p_sd15_openpose 写成 control_v11p_sd15_openpose.pth、多一个空格、或把下划线错成连字符。一旦不匹配,扩展不会报错,只是该单元权重实际为零。
要拿到服务端认可的名称清单,最稳妥的是调用 /controlnet/model_list 接口,它会返回已加载模型的准确字符串数组。自动化脚本应先拉取该列表,再用包含关系或全等判断来赋值,而不是硬编码猜测。以下 Python 片段展示了如何动态获取并选用模型:
import requests
base = "http://127.0.0.1:7860"
resp = requests.get(base + "/controlnet/model_list")
models = resp.json().get("model_list", [])
target = "control_v11p_sd15_openpose"
if target not in models:
# 退而求其次选第一个含 openpose 的
target = next(m for m in models if "openpose" in m)
print("use model:", target)
另一个易忽略的点是版本后缀。SD1.5 和 SDXL 的 ControlNet 模型不能混用,文件名里通常带 sd15 或 xl 标记。若基底是 SDXL 却填了 sd15 模型,后端可能直接跳过该单元。因此在拼写正确之外,还要确认架构一致。把模型名集中放在配置中心,配合启动时的列表校验,能杜绝大部分空载调用。
本地最小复现与逐步加参的排查法
当线上调用无效时,不要反复修改业务 prompt 或采样参数,那只会掩盖真正问题。更好的策略是在本地用 curl 或 Postman 构造一个最小请求:只带一张简单输入图、一个明确 module 和一个确定存在的 model,观察返回图是否受控。如果最小请求生效,说明原系统的拼接逻辑有干扰字段;若最小请求也无效,则聚焦服务端模型加载与扩展版本。
逐步加参指从单单元开始,确认生效后再叠加第二个控制单元、再调 weight 和 guidance。每加一项就比对一次出图差异,能精准定位是哪一层配置被忽略。下面给出一个用 curl 发最小请求的示例,注意 enabled 必须为 true 且 input_image 要真实可解码:
curl -X POST http://127.0.0.1:7860/sdapi/v1/txt2img
-H "Content-Type: application/json"
-d '{
"prompt": "a person",
"alwayson_scripts": {
"controlnet": {
"args": [{
"input_image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+M8AAAMBAQDJ/pLvAAAAAElFTkSuQmCC",
"module": "openpose",
"model": "control_v11p_sd15_openpose",
"weight": 1.0,
"enabled": true
}]
}
}
}'
这种排查方式比通读源码更快,也更容易写进团队排错手册。结合前两节的结构校验与名称校验,绝大多数“ControlNet 无效果”的工单都能在半小时内关闭。记住,接口静默失败是常态,主动拿清单、打结构、做最小复现,才是稳定的调试习惯。
ControlNetAPI_payloadmodel_name修改时间:2026-08-18 04:12:29