
嵌入式玩无线图传说来说去就是三件事画面从哪来、数据怎么传、终端怎么看。我这次用 ESP32-S3 把一只普通的 USB 摄像头改造成了 WiFi 图传设备整个链路从 USB UVC 取流、MJPEG 解码转发、再到网页实时预览全部自己抠出来没用任何现成图传模块也不依赖云平台。项目跑通之后手机浏览器打开一个 IP 就能看画面延迟大约在 150ms 到 300ms实测 320x240 分辨率下能稳定跑到 15 帧左右640x480 下也有 8 到 10 帧。这个方案特别适合做遥控小车视觉回传、室内巡检机器人、门禁抓拍这类应用也适合想从底层开始理解视频是怎么从一块摄像头模组走到浏览器里的的嵌入式开发者和全栈爱好者。为什么说这是个全栈项目因为它横跨了硬件接线、USB Host 协议栈、UVC 视频类协议、MJPEG 帧处理、TCP/IP 网络和前端取流展示这几层。任何一个环节脱节画面就是黑的、花的、卡的。下面我把整个实现思路、选型逻辑、固件核心代码流程和踩坑记录完整写出来希望能帮你少走几个月的弯路。1. 项目设计与整体选型1.1 为什么偏偏选 ESP32-S3这几年做图传很多人的第一反应是 ESP32-CAM毕竟自带摄像头接口代码一烧就能出图。但它有两个硬伤一是 OV2640 这类传感器是裸模组得自己拉 MCLK、SCCB、DVP 数据线布线稍微长一点就花屏二是摄像头的角色被焊死在板子上想换镜头、换传感器基本等于重新画板。ESP32-S3 解决了我最痛的点内置 USB OTG 控制器可以 Host 模式直接接标准 UVC 摄像头。市面上几十块钱的免驱 USB 摄像头插上就能枚举不需要关心传感器内部寄存器、不需要对齐时序UVC 协议帮你把摄像头这个接口统一了。而且 S3 是双核 Xtensa LX7主频 240MHz自带 512KB SRAM很多开发板还配了 8MB 到 16MB 的 PSRAM这对视频帧缓冲来说太关键了。选 S3 还有一个原因是它同时有 WiFi 和 BLE 5.0。虽然实际图传时我建议关掉蓝牙或降低蓝牙负载因为 2.4G 频段要共用天线BT 和 WiFi 一起跑会有频带争抢实测吞吐会掉一截但保留 BLE 意味着以后可以扩展蓝牙遥控、低功耗唤醒这类功能方案的延展性比经典的 ESP32 强不少。1.2 摄像头选型接 UVC 摄像头还是裸传感器项目里我对比过两条路。一条是直接用 OV5640、OV2640 这类裸传感器接到 ESP32-S3 的 DVP 或 SPI 接口优势是分辨率可以做得比较高、帧率可控但驱动工作量非常大。SCCB 寄存器序列、时钟树配置、帧同步信号对齐随便一个环节不对就是黑屏或者碎帧而且每个传感器的手册都有几百页试错成本很高。另一条路就是项目采用的 UVC 摄像头也就是 USB Video Class 设备。标准 UVC 摄像头内部其实是传感器 视频处理芯片 USB PHY的合体对外只暴露标准接口你通过控制传输去查询它的能力和参数通过等时传输或批量传输拿视频流。相当于你把传感器驱动的脏活累活外包给了摄像头内部的芯片和厂商固件你只需要处理协议层面的东西。实测下来罗技 C270、普通免驱摄像头、甚至 PDD 上十几块的 USB 工业摄像头模组都能跑关键是要确认它们支持 UVC 1.1 或 1.5并且能在 Full Speed 模式下工作。这里必须说一个很多新手不知道的坑ESP32-S3 的 USB 是 USB 1.1 Full Speed最高只有 12Mbps不是 USB 2.0 High Speed。所以别指望它能跑满 1080p 的原始视频流你必须选自带 MJPEG 硬件压缩输出的摄像头靠压缩后的数据量才塞得进这 12Mbps 的管子。1.3 全栈链路拆解图像到底怎么从镜头走到浏览器我把这套系统拆成了四层每一层都有明确的职责排查问题时按层切分非常高效。第一层是信号采集层也就是 UVC 摄像头内部完成的事镜头进光、传感器感光、ISP 处理、最后用 MJPEG 压缩输出。第二层是 USB Host 传输层ESP32-S3 跑 USB Host 栈枚举设备、解析 UVC 描述符、协商接口和端点、启动 isochronous 传输把一帧一帧的 JPEG 数据搬到内存。第三层是固件转发层把收到的 JPEG 帧存进 PSRAM 环形缓冲再通过 HTTP 或 TCP 协议把数据包上 WiFi 链路发出去。第四层是应用展示层手机上用浏览器访问固定 IP通过 multipart/x-mixed-replace 机制不断收到新的 JPEG 帧浏览器自动刷新形成连续画面或者写一个简单的 App 去解析 TCP 流。这四层里最容易被低估的是第二层和第三层的衔接。USB 传输是突发性的一帧数据可能在极短时间内到达但 WiFi 发送是离散的、带速率的。如果中间没有足够深的缓冲和合理的流控要么丢帧要么延迟暴增。我后面会用一整节讲这个缓冲设计。2. 硬件准备与接线细节2.1 器件清单与预算器件型号/规格参考价格用途主控板ESP32-S3-DevKitC-1 或带 USB Host 引出的 S3 开发板30-60 元主控注意要选引出 GPIO19/20 的板子USB 摄像头支持 UVC、支持 MJPEG 的免驱摄像头20-100 元视频采集罗技 C270 这类经典款最稳USB OTG 转接Type-C 转 USB-A 母座或 USB Host 扩展板5-15 元把 S3 的 USB 信号转成标准 USB-A 口电源5V/2A USB 电源或锂电池升压模块10 元USB 摄像头峰值电流可达 400-500mA必须外部独立供电可选USB Hub 芯片模块10 元以后想同时接鼠标、键盘、摄像头做 HIDUVC 复合设备时用我最终用的板子是 ESP32-S3-USB-OTG 那类板载一个 Type-C 口直接连到芯片的 GPIO19 和 GPIO20这样不用自己飞线插一个 OTG 转接头就能接摄像头非常省事。如果你手里的板子只引出了排针也没关系把 GPIO19 接到 USB-A 座的 D-、GPIO20 接到 D再共地即可。2.2 引脚与电源最容易烧板子的一步USB Host 的接线本身不复杂但有几个细节必须注意。首先是 D/D- 两根数据线的走线尽量短不要超过十厘米长了会引入信号抖动导致枚举失败。第二这两根线是差分信号虽然没有严格规定要等长但最好保持长度接近别一根飞了 20 厘米另一根只飞了 2 厘米。真正容易出事的是电源。USB 摄像头的标准供电是 5V工作电流从 100mA 到 500mA 不等启动瞬间还会有一个冲击电流。很多 S3 开发板的 5V 引脚是直接来自 USB 输入的如果摄像头和开发板共用一个电源输入启动时摄像头抢电主控可能直接掉电复位。我的做法是开发板单独用一条线接 5V/2A 适配器摄像头用另一路 5V 供电数据线共地。简单说电源分供、地线共接这是最稳的组合。2.3 实测比对我用过的几款摄像头表现摄像头分辨率支持MJPEG 全速模式320x24015fps640x48010fps备注罗技 C2701280x720支持稳定稳定最推荐兼容性好某宝 9.9 元免驱摄像头640x480支持稳定偶尔掉帧便宜够用OV5640 USB 模组2592x1944支持稳定稳定画质更好但价格高一点普通电脑摄像头1280x720部分不支持不稳定无法工作老旧型号多为 YUYV不推荐选摄像头时记住一句话优先选官方规格里标了MJPG或者YUV MJPEG双模式的如果一个摄像头只支持 YUYV 裸流那在 USB 1.1 下基本没法跑视频因为 YUYV 640x48010fps 的裸数据量就要 61Mbps远超 12Mbps 带宽。3. 开发环境与固件工程搭建3.1 VS Code 搭建 ESP32-S3 开发环境环境搭建其实没什么玄学但初学者经常栽在版本匹配上。我现在的标配是VS Code Espressif IDF 插件 ESP-IDF v5.2。用的 IDE 插件自动帮你管理工具链、Python 环境和编译链命令行党也可以直接用 idf.py。关键步骤如下安装 VS Code 和 Espressif IDF 插件后在插件面板里选Configure ESP-IDF Extension选择使用已有的 ESP-IDF或者让它自动下载。安装完以后在命令面板里调出ESP-IDF: Select Port和ESP-IDF: Set Espressif Device Target两个命令把 target 选为 esp32s3。编译前还要在板子的选择上注意如果板子带 PSRAM默认配置可能没有开启你需要用 menuconfig 把 Component config 里的 PSRAM 选项打开。我踩过的一个大坑是第一次编译 USB Host 相关例程时因为 IDF 版本太老缺少 esp_usb_host 组件连编译都过不了。这个组件从 ESP-IDF v5.1 开始才比较完善所以如果你的手头环境还是 v4.x我建议直接换 v5.2会省很多事。3.2 用官方 esp-usb 组件还是第三方组件USB 摄像头驱动这块目前主要有两个方案路线。第一个是用乐鑫官方例程 espressif/esp-usb 仓库里的 host/uvc 示例这个示例比较贴近底层你能看到 USB 枚举、接口选择、视频流启动的完整流程非常适合学习。但官方示例偏向于验证功能代码风格比较精简离直接用还有距离你需要自己补 HTTP server、WiFi 连接这些外围逻辑。第二个路线是 atomic14 的 esp32-usb-camera 项目这个项目把 UVC 主机封装成了比较友好的接口提供现成的 MJPEG HTTP server甚至配套了一个 Flutter 的 App。我实际参考过它的设计思路尤其是帧缓冲处理和推流逻辑很值得读一遍。但我不建议完全照抄因为它的代码耦合了比较多的个人偏好比如固定的 HTTP 端口、固定的分辨率协商而自己的项目往往需要调整。我的建议是初学者先从官方例程跑通 USB 取流让你对 UVC 协议栈有体感然后参考 atomic14 的工程结构把推流、缓冲、WiFi 配置这些业务逻辑自己写一遍虽然慢但后面对你排查问题、加功能会很有帮助。3.3 工程目录和关键配置项一个精简的工程结构大概长这样esp32_usb_cam/ ├── CMakeLists.txt ├── main/ │ ├── CMakeLists.txt │ ├── app_main.c # 初始化 WiFi、USB、HTTP server │ ├── uvc_stream.h # UVC 流启动接口 │ ├── http_mjpeg_server.c # MJPEG over HTTP 推流实现 │ └── ring_buffer.c # 帧级环形缓冲 └── components/ └── esp-usb/ # 官方 esp-usb 组件或自修改版本 ├── host/uvc/ └── ...menuconfig 里我最后确认的关键项有这几个开启 PSRAM根据板子选 Octal 或 Quad关闭不必要的 BT 协议栈栈如果你暂时不用蓝牙直接关掉可以给 WiFi 让出一点资源增大 TCP 发送 buffer 和 socket 超时选项避免推流时出现半开连接导致卡死在 WiFi 相关配置里关闭 power save 模式这个特别重要后面排查吞吐问题时还会提到。4. 固件核心逻辑从 UVC 取帧到 WiFi 推流4.1 UVC 协议到底在做什么先做一个生活化类比。UVC 摄像头就像一个饭店后厨UVC 协议就是菜单。你不需要去研究厨房里每个厨师怎么做菜你只需要看菜单上写了能做什么菜和价格然后点菜。对应到技术上ESP32-S3 作为一个 USB Host启动后第一步是枚举设备通过控制传输读取设备描述符、配置描述符、接口描述符从中找到 Video Streaming 接口和它的端点地址然后通过 Video Control 接口里的 Camera Terminal 去发控制命令比如设置亮度、曝光最后通过 Video Streaming 接口的 Probe/Commit 控制协商出你要的分辨率、像素格式、帧率。协商完成后视频数据就开始从 isochronous 端点或者部分摄像头用 bulk 端点哗哗地往主机里灌。有一类典型的坑是摄像头支持的带宽模式不一样有的只在 High Speed 下才开放高分辨率 MJPEG 流在 Full Speed 主机下只给你一个低分辨率选项。这时候你强行申请高分辨率设备会直接返回错误所以代码里要做一层降级策略申请失败就自动退到 320x240。4.2 帧缓冲设计为什么必须上双缓冲和环形缓冲视频流是一个持续不断的数据流USB 中断一上来DMA 可能已经写满了 512 字节的 buffer。如果你的代码在中断回调里做 JPEG 帧拼接和推流那系统很快就会卡死。正确做法是USB 传输回调只负责把数据拷贝到一个大内存块里同时做好帧边界检测。MJPEG 流的帧边界通常靠 JPEG 的 SOIFFD8和 EOIFFD9标记来判断检测到 FFD8 就认为一帧开始检测到 FFD9 就认为一帧完整可以把这一块数据提交给下一级处理。我的实现里预分配了 4 帧的环形缓冲每帧大小在 PSRAM 里动态分配。摄像头输出 320x240 的 MJPEG 帧平均大小在 8KB 到 20KB 之间我在 PSRAM 上留出每帧 64KB 的缓冲足够容纳最大帧。环形缓冲的好处是USB 生产者和 WiFi 消费者之间解耦生产者写满了就覆盖最旧的一帧消费者读取时永远能拿到最近一帧这一点对降低延迟非常关键。这里有一个常见的误区有人为了不丢帧把缓冲加得很深比如 10 帧甚至 20 帧看起来画面流畅了但延迟也跟着涨到一秒以上遥控小车根本没法开。图传系统要的是最新一帧优先而不是所有帧都送到所以我的策略是缓冲深度控制在 2 到 4 帧之间消费者发现来不及处理时直接丢弃旧帧、取最新帧保证实时性优先。4.3 WiFi 推流方案HTTP MJPEG、TCP Stream 还是 UDP传输层方案我前后试了三个最后留下 HTTP MJPEG 作为默认方案同时保留一个 TCP 二进制的口子给 App 用。HTTP MJPEG 的原理非常简单HTTP 响应头里设置Content-Type: multipart/x-mixed-replace; boundaryframe然后每收到一帧 JPEG 数据就拼上一个 boundary 分隔头发出去。浏览器收到后会一直保持连接每一段新的 JPEG 到达就自动替换显示区域不需要 JavaScript 和插件。这个方案的好处是调试极方便Chrome、手机浏览器、VLC 全能直接打开零依赖。TCP Stream 方案是自定义一个帧封装比如 4 字节帧长度 JPEG 数据接收端自己解析。它比 HTTP 省掉了 HTTP 头部的重复开销理论上吞吐更高、延迟更低适合写原生 App 的时候用。代价是调试困难得自己写客户端解析代码。UDP 方案则适合极端追求低延迟的场景把 JPEG 帧切成分片直接扔接收端做顺序重组和丢包重排但会引入花屏和复杂度项目初期不推荐。方案延迟吞吐开销调试难度适用场景HTTP MJPEG中等略高极低浏览器演示、快速验证TCP Stream较低低中原生 App、二次开发UDP 分片最低最低高高要求低延迟场景需自研重排实际项目里我默认开 HTTP MJPEG同时把 TCP 二进制流挂在 8080 端口两个并存互不干扰写 App 时直接连 8080。4.4 性能瓶颈与调优12Mbps 的 USB 和 2.4G WiFi 的博弈很多人在这个项目上折腾半天总觉得代码有问题但其实是没认清楚硬件天花板。ESP32-S3 的 USB 是 Full Speed 12MbpsWiFi 是 2.4G 802.11n单天线 MCS7 在 20MHz 频宽下的理论吞吐也就 72Mbps但实际使用中 TCP 吞吐能稳定到 10 到 15Mbps 就算不错了。所以整条链路的瓶颈压根不在代码而在 USB 带宽和 WiFi 干扰。按照图像数据量来算320x240 的 MJPEG 一帧平均 10KB12Mbps 的 USB 带宽理论上一秒能传约 150 帧刨掉 USB 协议开销、分包开销实际传 30 帧没压力。但如果把分辨率提到 1280x720一帧就到了 80KB 到 150KB就算摄像头能输出 30fpsUSB 带宽也只够传 8 到 12 帧这时 Wi-Fi 端再发生一次重传延迟立刻爆表。所以我的调优策略是分辨率优先保 320x240帧率目标 15fps如果有余量再试着调 640x480 并把帧率降到 8fps。同时在代码里做一个很土但很有效的功能根据 PSRAM 剩余量和 USB 传输速率动态调整请求的分辨率和帧率网络差就自动降质量网络好就回升画面从而不会完全卡死。4.5 推流核心代码骨架下面是一个简化到只剩核心流程的伪代码重点看数据是怎么从 UVC 回流传到 HTTP handler 的// UVC 帧回调USB Host 栈收到完整一帧 JPEG 时调用 void uvc_frame_cb(uint8_t *data, size_t len) { // 写入环形缓冲覆盖最旧帧 ring_buffer_write(g_frame_buf, data, len); g_frame_count; } // HTTP MJPEG handler static esp_err_t mjpeg_handler(httpd_req_t *req) { httpd_resp_set_type(req, multipart/x-mixed-replace; boundaryframe); while (1) { if (ring_buffer_has_frame(g_frame_buf)) { int len ring_buffer_read(g_frame_buf, g_jpg_buf, sizeof(g_jpg_buf)); char part_head[64]; int n snprintf(part_head, sizeof(part_head), --frame\r\nContent-Type: image/jpeg\r\nContent-Length: %d\r\n\r\n, len); httpd_resp_send_chunk(req, part_head, n); httpd_resp_send_chunk(req, g_jpg_buf, len); httpd_resp_send_chunk(req, \r\n, 2); } else { vTaskDelay(pdMS_TO_TICKS(20)); // 等待下一帧 } } return ESP_OK; } // WiFi 初始化时务必关闭省电 esp_wifi_set_ps(WIFI_PS_NONE);这套骨架的要点在 httpd_resp_send_chunk它支持持续发送数据而不用重新初始化响应。在这一堆代码跑通之后你的浏览器里能看到的就已经是一段连续的视频流了。5. 联调、性能优化与全栈验证5.1 浏览器直接拉流验证联调第一步永远是把链路分成两段看。先不接 Wi-Fi直接在 ESP32-S3 上把 UVC 取到的帧通过串口或者本地 LED 状态打印出来确认 USB 取流是好的再进 Wi-Fi 阶段。打开这些日志后使用串口监视器观察如果能看到摄像头枚举成功、分辨率协商成功、帧计数在增加说明底层没问题。然后配置好 WiFi 连接你自己的路由器或者热点烧录程序后观察设备拿到了 IP。随便拿一台手机连同一个局域网在浏览器地址栏输入 ESP32-S3 的 IP正常情况瞬间就能看到画面。如果端口不是默认 80记得拼上端口号。这一步成功意味着整条链路的数据面已经通了。此时你可以顺手试一下用 VLC 打开同一地址VLC 的网络流的兼容性和浏览器略有不同能帮你在后续开发中进一步定位问题到底是出在 HTTP 封装还是出在帧数据本身。5.2 小车场景怎么用把图传贴到遥控设备上这类图传最常见的落地方案是遥控小车。硬件上ESP32-S3 板子和摄像头做成一个独立的图传模块用两根线从电池取 5V 电然后和主控板通过串口或者 WiFi 协处理器通信。图传模块只管回传画面小车运动控制走它自己的遥控链路两边互不干扰。我实际测试过这种组合小车跑起来的时候摄像头画面会出现明显的运动模糊和卡顿但 15fps 的下限仍然能让你看清方向如果分辨率拉到 640x480画面细节是好了但延时涨到 500ms 以上拐弯的时候基本靠感觉所以最后还是退回 320x240。这套系统用于视觉反馈辅助而不是实时自动驾驶是完全够用的再往上的需求应该考虑更强的 SoC 和 5.8G 图传通道。5.3 帧率、延迟和画质的三角权衡图传系统的三个指标——帧率、延迟、画质在给定带宽下永远只能选两个。我把实测数据列成了一张表方便你按场景选配置配置组合帧率(fps)端到端延迟(ms)画面观感适用场景320x240 MJPEG, 低质量20-25120-180模糊但流畅快速移动、遥控320x240 MJPEG, 标准15-18150-250均衡默认推荐640x480 MJPEG, 标准8-10250-400清晰但略卡巡检拍照、静止监控640x480 MJPEG, 高质量4-6400-700很清晰但延迟高拍照、取证这个表是在同一房间没有微波炉干扰、Wi-Fi 信号 -50dBm 左右测出来的换了环境数值会有浮动。我的建议是产品化的时候把这一组配置写死在 Flash 里用一个 GPIO 按键或者命令行参数切换避免每次都要重新编译改分辨率。6. 踩坑实录与常见问题速查6.1 枚举失败摄像头不认设备列表都是空的这个是我调试过程中遇见次数最多的问题。排查优先级按下面这个顺序来第一电源是否稳定。尤其是那些 9.9 元的摄像头启动瞬间电流能冲到 600mA如果你的 5V 电源只有 500mA 输出USB 总线电压会瞬间被拉低设备直接就无法枚举。可以外接一个带电流输出的电源测试或者用示波器看 D 引脚有没有被拉高。第二数据线问题。D/D- 接反是常见错误另外线材过长或者用了劣质 OTG 线信号完整性不够也会导致枚举失败。尽量缩短线缆或者使用带屏蔽的 USB 线。第三UVC 兼容性。部分摄像头只支持 USB 2.0 High Speed在 ESP32-S3 的 Full Speed 下直接不工作。可以用 PC 上的 USB 树查看它的 device descriptor如果在 Full Speed 模式下无法列举那就只能换摄像头。6.2 WiFi 吞吐上不去、推流卡顿WiFi 吞吐问题经常和蓝牙共存、省电模式拉开关系。遇到推流卡顿先检查代码里有没有执行 esp_wifi_set_ps(WIFI_PS_NONE)这是关闭调制解调器省电模式的接口。省电模式开启时WiFi 射频模块会周期性地休眠导致接收和发送延迟变得非常不稳定视频流自然一卡一卡的。其次排查信道干扰。2.4G 频段在居民区非常拥挤WiFi、蓝牙、微波炉、无线鼠标全挤在一起。把路由器信道手动设到 1、6、11 中比较空闲的一个你可以在 PC 上用 WiFi 分析仪查看周围网络信道使用情况。如果项目可以接受设计成 ESP32-S3 开启 AP 模式自己创建热点让手机直连吞吐成绩会比连路由器好很多因为没有其他终端的竞争。最后要留意带不带蓝牙。如果你同时启用了 BLE推荐把 BLE 的连接间隔调大或者干脆不用蓝牙时全关掉。实测开蓝牙后本来 12Mbps 的 TCP 吞吐可能直接掉到 6Mbps。6.3 花屏、图像撕裂、绿屏画面花屏多半是帧拼接不完整。MJPEG 流在传输过程中如果 USB 端点和 WiFi 端产生丢包就可能只有 FFD8 没有 FFD9拼出来的是一半的 JPEG。解决手段是加一个帧完整性校验发现帧不完整就直接丢弃不要硬塞给 HTTP 输出绿屏往往就是往浏览器里喷了半个 JPEG 导致解码器崩溃。另一个更隐蔽的原因是缓冲区覆盖。当生产者速度大于消费者速度时环形缓冲会覆盖正在读取的数据导致帧内容错位。我加了一个互斥锁和读者写者赛跑检测遇到这种竞争直接报错统计就能抓到元凶。6.4 常见问题速查表现象可能原因解决办法枚举失败供电不足、D/D- 接反、线缆过长、设备不支持 FS独立 5V 供电、缩短线缆、换 UVC 摄像头、查设备描述符推流卡顿WiFi 省电开启、信道拥挤、蓝牙共存关 power save、换信道、关 BLE 或调大连接间隔花屏/绿屏帧不完整、缓冲覆盖加帧校验丢弃异常帧、加互斥锁、控制缓冲深度浏览器黑屏但串口有帧数HTTP header 没设置正确检查 multipart/x-mixed-replace 和 boundary 拼接格式分辨率协商失败摄像头不支持 Full Speed 下的高分辨率手动降级到 320x240 或 640x480延迟越来越高缓冲堆积、消费者性能不足帧优先策略、减少缓冲帧数、提高主频到 240MHz项目做到这一步其实已经具备了一个完整图传设备的雏形摄像头即插即用、WiFi 传流、手机网页看画面、可调分辨率、可换帧率。我在实际测试中的体会是这类项目真正花时间的不是代码量而是理解USB 1.1 只有 12Mbps这个天花板以及如何用一个可控的帧缓冲去匹配两个速度不对等的世界。后面如果你想继续扩展可以在 ESP32-S3 上做运动检测、把 JPEG 帧送到边缘 AI 做目标识别或者增加 SD 卡录像回放功能这套图传链路就是现成的数据来源整个全栈的地基已经搭好了。