智能车视觉工程架构:从摄像头采集到路径规划的完整开源方案

发布时间:2026/10/5 1:22:13

智能车视觉工程架构:从摄像头采集到路径规划的完整开源方案 做智能车视觉的朋友应该都有同感算法链路的每一步单独看都不难难的是把采集、处理、识别、规划装进一套能在赛道上稳定跑完的工程里。Soberup战队最近把我们在视觉组上沉淀的完整视觉路线开源了这是系列第一篇先讲整体视觉路线与工程结构。这篇内容主要面向两类人一是准备参加智能车竞赛、想从零搭建摄像头视觉方案的队伍二是想参考一套完整嵌入式图像处理工程如何组织代码的开发者。读完之后你能对我们为什么选这套架构、每个模块承担什么职责、代码文件怎么摆放有清晰的认知也能直接拿目录结构去对齐你手头的项目。1. 视觉路线整体设计1.1 先划任务边界再选传感器和主控Soberup战队的视觉方案跑在智能车竞赛的视觉组赛道上核心任务是通过前置摄像头采集赛道图像识别赛道边界、中心线和典型元素十字、环岛、坡道、锥桶等输出可靠的循迹路径与目标速度给控制模块。很多人一上来就纠结摄像头分辨率、主控算力但实际经验是先划任务再选硬件。赛道识别本质上是结构化环境下的感知问题场景几乎固定浅色背景、深色赛道或者反过来深色背景、浅色赛道取决于当届规则。这意味着我们不需要做通用目标检测也不需要跑神经网络用经典图像处理就够了。选了经典视觉方案好处很明显调试逻辑透明、参数可控任何失效情况都能定位到具体环节代码量可控在竞赛MCU上能把帧率稳定跑满。缺点也明确对光照和摄像头安装角度的变化比较敏感所以工程上必须配套曝光自适应和畸变校正这部分后面会展开。顺带说一下不少队伍会纠结是否上OpenMV或者K210这类带现成视觉库的模块。我的看法是模块跑demo确实快但真到比赛现场你需要精确控制每一帧的处理流程需要知道每个参数在车模姿态下的物理含义。从寄存器层开始做前期慢中期顺后期不会被底层限制卡死。Soberup选的是后者这也是整个开源路线的起点。1.2 硬件链路选型背后的逻辑硬件链路我们最终用的是全局快门灰度摄像头总钻风这类MT9V032方案加STM32H750主控中间走DCMI接口和DMA搬运。这里每个选择背后都有一堆实际踩出来的坑。摄像头必须选全局快门不能用手机摄像头那种卷帘快门。车跑起来以后卷帘快门每一行曝光时间不同图像会出现明显的果冻扭曲直的边界线会变成斜线后续识别全乱。灰度摄像头看起来信息少反而对赛道识别是优势处理数据量小帧率更高算法也简单不需要做彩色空间转换。彩色图像在自然光下很容易受色温和反光影响灰度只关注亮度赛道边界本质就是亮度阶跃信息匹配度反而更高。主控选STM32H750核心考虑是性能余量和外设匹配。480MHz的主频在竞赛MCU里属于第一梯队DCMI可以直连数字摄像头输出DMA搬运图像不占CPU。内置的大缓存和ART加速对图像处理这类循环密集代码帮助明显。如果你的主控性能弱一些也不用慌我们代码里多数模块做了性能适配但要做好分辨率下降或丢帧的准备。还有一个常被忽略的点供电和串口。摄像头、主控、无线模块共地必须做好不然图像上会随机出现横纹看起来像算法问题实际是电源噪声。建议给摄像头单独用一路LDO并在靠近摄像头电源脚放100nF和10uF电容这部分我们后面细说。1.3 分层架构与可插拔的取舍工程上我们最终定了四层结构应用层、算法模块层、驱动层、配置层再加一个独立于板端的PC调试工具链。这个分层不是拍脑袋定的是从踩坑里总结出来的。最开始我们把图像处理和控制逻辑写在一个while循环里确实简单跑到第二个星期就崩溃了。每次改一个阈值都要把所有代码重新编译烧录出问题分不清是算法问题还是调度问题。后面重构成了任务式模型图像采集任务、图像处理任务、通信任务各自独立用状态机调度。采用可插拔设计指的是每个视觉处理环节都通过接口定义替换实现不影响其他模块。比如二值化有固定阈值、大津法、自适应灰度窗口三种实现在配置文件里切一个宏就能换。为什么必须这样因为不同场地光照条件完全不一样你需要现场换策略而不是重新烧代码。这个设计也为后面的离线复现机制打了基础调试章节我会细说。2. 工程目录结构与模块划分2.1 目录树与文件职责速览放出我们工程目录的核心部分你可以直接对照手头的项目看。SoberupVision ├── app │ ├── main.c │ ├── vision_task.c │ ├── control_task.c │ └── comm_task.c ├── modules │ ├── camera │ │ ├── mt9v03x_cfg.c │ │ ├── dcmi_dma.c │ │ └── image_buffer.c │ ├── image │ │ ├── preprocess.c │ │ ├── binarize.c │ │ ├── perspective.c │ │ ├── edge_detect.c │ │ ├── boundary_search.c │ │ └── element_detect.c │ ├── planner │ │ ├── path_gen.c │ │ ├── speed_plan.c │ │ └── curvature.c │ └── comm │ ├── uart_protocol.c │ └── telemetry.c ├── drivers │ ├── sccb.c │ ├── timer.c │ ├── pwm.c │ └── encoder.c ├── config │ ├── vision_config.h │ └── track_params.h ├── tools │ ├── img_logger.c │ └── replay_parser.py └── docs看这个结构重点是modules和config分开。算法代码通过参数文件调整而不是在代码里到处找魔数。新人接手项目时先看config再看modules里的函数名基本能说清整个流程。文件命名也是按功能来的凡是带_task的都是调度入口凡是带具体算法名的都是纯计算函数驱动层只放寄存器操作这样找代码非常快。2.2 以图像为核心的数据流设计整个工程的数据流围绕一帧图像走。摄像头把图像通过DMA搬运到双缓冲vision_task从当前空闲缓冲取一帧依次走预处理、二值化、透视变换、边界搜索、元素识别最后生成路径点给control_task同时comm_task把关键中间结果通过串口发出去。双缓冲是这里的关键。图像采集是持续不断的如果处理任务直接读当前帧很容易出现上半帧是旧的、下半帧是新的混合图像。所以我们用ping-pong缓冲DMA正在写缓冲A时CPU处理缓冲B完成后再交换。这说起来简单做起来有个容易踩的坑切换缓冲指针必须放在DCMI的帧中断里做放在主循环里做会偶发指针竞争。每个任务之间用单向数据接口连接图像缓冲只由采集任务写入、处理任务读取控制任务只收路径点。这种单向依赖能避免很多隐形竞争。最初我们也试过全局变量互相读三个任务都访问同一个状态量结果车一颠簸偶尔就死机查了两天才发现是竞态。所以后来规定模块间的共享数据必须经过接口函数内部用临界区保护外部只读快照。2.3 配置系统把参数从代码里解耦出来config/vision_config.h里只放视觉处理参数track_params.h放赛道有关的几何参数摄像头安装高度、俯仰角、车宽、摄像头到前轮距离等。这个分离非常实用。视觉参数比如二值化阈值、ROI区域、透视变换比例在调车时需要经常改赛道几何参数一旦标定好基本不动。之所以强调配置分离是因为赛前现场时间异常宝贵。你大概率会遇到光照突然变化、赛道材质与本地试跑不同等情况需要快速尝试多种参数组合。参数如果散落在代码里改一次编译一次一套流程下来三分钟就没了集中管理后通过上位机组播下发10秒就能换一套配置。我们把配置做成了结构体支持从串口接收参数更新并写入Flash重启不丢失。这个功能在后几轮调试里几乎救了我们整个队伍。配置系统还要考虑生效时机。直接全局替换参数会让正在处理的这一帧数据不一致我们采用当前帧处理完再应用新参数的机制。串口收到参数后只存入待更新区vision_task每帧处理完后检查一次待更新标志再整体加载。这样在线调参时画面不会因为参数突变闪一下也不会出现半套新参数半套旧参数的诡异状态。3. 核心视觉流程与关键实现3.1 图像采集与曝光策略视觉链路的第一步是把曝光这个变量抓在手里。很多队伍在室外强光或室内灯光下出现丢线问题九成出在曝光上而不是识别算法。MT9V032这类摄像头的寄存器可以配置增益、积分时间等效快门和亮度目标。我们写了一个简单的自动曝光算法每隔N帧统计一次图像平均灰度与目标灰度比如128比较按比例调整积分时间步进限制在一定范围内防止画面亮度突变。这里有一个很重要的细节自动曝光的目标灰度不能死板地固定最好通过赛道对比度来确定。比如深色赛道浅色背景的场景我们期望背景亮、赛道暗那就统计全图直方图判断两个峰的位置再取中间值作为曝光目标而不是直接设定128。这样在逆光、侧光场景下都相对稳。整体采集配置我们用的是188x120分辨率。很多新手不理解为什么不用更高分辨率理由很简单赛道识别是找边界边界本质是灰度突变位置太低看不清太高则每帧处理时间成倍增长。188x120在H750上处理约4ms到6ms搭配60帧/秒的采集可以保证单帧处理完还有充足时间做控制。帧率这块也提一句不是越高越好关键要稳。我们锁60帧如果某帧处理超时直接丢弃而非排队处理。丢了这一帧下一帧还是新的不会造成图像延迟累加。很多队追求100帧反而让CPU长期满载一旦出现中断抖动处理链就崩了。3.2 预处理灰度归一化、噪声抑制与图像校正采集到的是灰度图所以预处理第一步是把图像归一化到稳定范围。因为镜头边缘进光量不足模板四角往往比中心暗不处理的话边界搜索在四角区域会误判。我们用参考帧校正法上电后对着均匀白纸采集一张平场参考图存到内存运行时每个像素除以参考值再乘目标灰度。这个操作在188x120下全图遍历不到1ms换来的是整个视野内灰度一致。噪声处理上我们不着急用高斯滤波因为高斯滤波对快速移动下的细微边缘有钝化。实际测试下来用一个3x3中值滤波或者干脆不滤波在二值化后再做3x3形态学开闭运算效果反而更好。原因是赛道边界的阶跃性强中值滤波既能去孤立噪点又不会像均值滤波那样让边缘糊掉。接着是畸变校正和透视变换。摄像头的鱼眼或针孔畸变在图像边缘非常明显直接对畸变图像做边界搜索弯道内侧边界会向外侧偏移。我们在标定场拍了一张棋盘格用对称相机模型求畸变系数然后对每个输出像素建立查找表运行时用查表映射避免每帧算double坐标变换。查找表生成是在PC端做好的MCU上只做采样。透视变换同样用查表。原图里赛道在远处是一条窄带直接处理会丢失很多行信息用逆透视变换把图像投影到车体坐标系后赛道在图像里平行且宽度一致边界搜索的行扫描更稳定。但要注意逆透视不是越多越好投影太远会放大远端噪声。我们只投影到车前1.5m到2.5m的区域再远的部分边界可信度低路径规划时会给小权重。3.3 赛道边界搜索与中心线生成预处理完成后进入主线算法逐行搜索左右边界。我们从图像底部离车最近向上搜索每次搜索以上一行的左右边界为起点在一定范围内找灰度突变点得到当前行的边界最后左右边界取中点形成中心线。这个从底部逐行向上配合搜索窗口的做法是视觉循迹性能稳定的核心。它相当于利用赛道边界在相邻行之间是连续变化的先验把二维搜索简化成一维跟踪。搜索窗口宽度设置很讲究太宽容易串进噪点太窄弯道一急就追丢。我们按图像行数动态设置底部行窗口宽顶部行窗口窄。底部离车近边界位置可信搜索范围可以放40像素顶部远处干扰多搜索范围缩到20像素以内。边界搜索的状态机也要考虑丢线恢复。连续几行搜索不到边界不代表赛道没了可能是反光造成的局部断裂。我们的策略是单行丢失时沿用上一行边界位置插值补上连续超过5行丢失则进入重搜模式从图像最底部重新初始化左右边界。这样既抗噪又不会让误差累积到失稳。元素识别放在边界搜索之后而不是之前。先保证普通赛道的边界连续再在边界变化异常的地方标记候选区域用特征规则判断是不是十字、环岛、坡道。比如十字的特征是左右边界在某处同时出现向外的大跳变环岛的特征是一侧边界连续、另一侧突然出现断口。这个顺序保证了每个元素识别都基于可靠的边界数据不会被孤立噪点误导。3.4 路径生成与曲率速度规划边界和中心线不是最终交给控制的量控制需要的是带曲率的目标路径点。我们把中心线点列转换到车体坐标系下用最小二乘法拟合圆弧算出等效曲率。实际因为计算量限制用的是三点定圆的简化版取近、中、远三个中心线点解一个圆形方程。三个点的选取要躲开元素区域的跳变点否则算出的曲率会突变。曲率出来后速度规划就好做了。我们的策略是大直道冲刺、小弯道巡航、急弯和环岛提前减速。具体用一张查表根据曲率-半径映射速度再用电机余量做限幅。查表比在线公式快也更容易现场调你想改弯道限速直接改表里的数值就行。这里有个容易被忽略的点减速时机比减速量更重要。必须根据曲率变化趋势提前一段距离减速否则车到弯心里才减横摆已经拉不住了。我们把曲率的低通滤波做到速度规划里让速度变化曲线不跳变。从路径曲率到速度输出整个链路的延迟要控制在10ms以内否则车会明显滞后。4. 调试环境与效率工具4.1 离线串帧复现机制智能车调试最痛苦的事是赛道上问题复现不了跑了三圈出一次丢线回到电脑前怎么都找不到原因。所以我们从第一天起就要求所有中间结果可以离线重放。实现方式是在tools/img_logger.c里把每帧原始图像、处理后的二值图、边界坐标点串成一个包通过串口写到SD卡或上位机。关键帧带时间戳回放工具replay_parser.py读取log逐帧显示每一步的输出。有了这套机制赛道上出问题只需要把log存下来晚上回去可以一帧一帧看是曝光突变还是搜索窗口丢失一目了然。这里想强调所有中间结果可见带来的杠杆效应。当队友跟你反馈第三圈右弯丢线你不用再靠猜直接把那一帧的二值图、透视变换图、边界搜索点三张图对齐看10分钟能定位到是预处理问题还是搜索参数问题。我们的历史记录中有80%的问题通过回放确认比现场看车调试效率高太多。日志模块要注意格式设计。我们用了固定头部加可变长度数据的格式头部存帧号、时间戳、参数版本号后面按类型存图像或坐标数组。参数版本号特别重要因为现场经常换参数没有版本号的话回放时根本不知道这帧用的是哪套配置再对比数据就没有意义了。4.2 上位机回传与在线调参光能离线看还不够在线调试同样需要。我们用串口做了一套精简的二进制协议每秒回传关键数据当前帧号、曝光时间、平均灰度、左右边界丢失状态、曲率输出。上位机把这些信息绘成曲线现场调车时盯着曲线比盯着路面更准确。因为肉眼很难分辨图像灰度细微变化但灰度曲线轻微抬升你有感觉。在线调参是另一个省时间的点。车辆跑起来以后通常是速度、转向和图像参数耦合的问题如果每次调参数都要停车烧录一次调车窗口根本做不了几次实验。我们把视觉参数全部映射成协议字段比如阈值、曝光目标、搜索窗口宽度都可以在车跑动时通过上位机调整然后车上的配置系统实时更新。这个功能建议所有做视觉的队都做哪怕先做阈值一个参数。选一个单独的调试串口波特率115200或更高确保不和处理任务抢数据。上位机工具不用做得多花哨能用Python的串口库加一个简单的Matplotlib窗口就够。把实时曲线和按键调参绑在一起像打游戏一样调车效率一下就上来了。4.3 性能预算与耗时统计工程结构里的每一个算法我们都会在注释里写清它对单帧耗时的预算。比如二值化1ms、透视变换查表0.5ms、边界搜索2ms、元素识别1ms。为什么要做预算因为视觉处理是强实时链总耗时必须小于帧间隔否则会进入积压状态图像越来越旧动作自然慢半拍。统计耗时用的是DWT周期计数器比定时器更精准能在不打断DMA的情况下获得每个子函数的真实执行时间。每隔几帧把耗时表通过串口发上去观察是否有时间抖动。如果哪次帧耗时突然变大基本是缓存未命中或者中断嵌套要尽早查而不是视而不见。注意D-Cache可能影响耗时统计的准确性。如果代码和数据都在TCM区不受Cache影响但一旦图像缓冲被DMA写到外部RAMCPU读这些地址时可能触发Cache miss耗时突然翻倍。我们最终把图像缓冲放在紧密内存区并且为关键任务开启了编译器的-O3优化配合循环展开宏整体帧耗时压得很稳。5. 常见问题与排查技巧实录5.1 高频问题速查表整理一份直接可用的排查表都是我实际在调车现场遇到过的情况。现象大概率原因建议排查路径弯道丢线搜索窗口过窄或曝光突变关掉自动曝光看固定阈值下表现十字识别误判边界连续性判断逻辑太敏感降低连续跳变阈值先回放确认逆光下全图过亮自动曝光目标受强光源干扰改用直方图双峰法确定曝光目标环岛回环处跳胎路径曲率拟合点选得太远减小远端拟合点权重提前减速处理耗时突然变大某行扫描循环越界检查边界搜索的数组越界保护帧率上不去图像尺寸过大或滤波占用过高先用188x120跑通全链路图像有拖影卷帘快门或全局快门配置错误确认摄像头寄存器为全局快门模式画面上有随机横纹供电噪声或地线环路摄像头独立LDO改善共地回放数据与现场不一致参数版本号没记录在日志头部加入参数版本字段5.2 三个容易翻车的细节第一个是数组越界。边界搜索代码里如果上一行边界丢失继续往上所有行都可能无界可循直接跳到图像边缘会导致数组越界。我们在所有索引访问前加边界熔断连续丢线超过一定行数就进入重搜状态重新从底行初始化窗口。这样不会因为越界跑飞控制任务。第二个是缓存一致性问题。STM32H750的D-Cache必须小心DMA写入的图像数据被D-Cache缓存后CPU可能读到旧数据。如果开了D-Cache需要在DMA写完成中断里做clean和invalidate。这个坑我们调了整整一个下午最后关掉D-Cache就好了但性能会有损失。所以后来干脆把图像缓冲放到TCM区域避开Cache一致性整个问题。第三个是配置参数没做合法性校验。现场用上位机调参手一抖发了一个极小的搜索窗口车直接放飞。我们最后在配置接收函数里加了范围限制每个参数都有最小最大值和步进判断非法数据直接丢弃并回传错误码。这个防护很基本但能省掉很多炸车现场。5.3 排查思路的正确打开方式出问题时很多队员的第一反应是改参数但其实最该做的是确定问题层。我的排查顺序是先确认图像输入是否正常再看预处理输出是否符合预期然后看边界搜索有没有跟丢最后才怀疑路径生成。每层都要有可视化的输出或日志字段否则排查就是盲猜。举个例子如果弯道丢线先看曝光和帧率排除硬件层再回放二值图看边界是否被噪声打断如果二值图正常问题大概率在搜索窗口动态设置上。这个顺序看着简单但能把排查时长从几个小时压到十几分钟。赛前调车窗口本就紧张有条理的排查流程比任何技巧都管用。还有一点改动要一次只改一个变量。很多队现场着急阈值、曝光、窗口宽度一起调结果车好了一圈根本不知道是谁起的作用。我们强制要求每次记录改了什么参数跑一圈后对比这次调车才算是有效实验。这个习惯坚持一个赛季你的参数库会变得非常珍贵。结尾我这几年调视觉车最大的感受是视觉方案的价值不在于某个单独算法有多高级而在于整个工程结构能不能让你在压力和混乱中快速定位问题。Soberup这套路线开源出来第一篇先把总览和结构讲清楚也是希望更多队伍少走我们在架构上反复折腾的弯路。如果你正打算搭一套自己的视觉工程建议不要一上来就写算法先花半天把目录结构、模块边界、调试入口定下来。后面再填算法代码时你会发现所有问题都有了固定的归处。后续系列会把摄像头标定、边界搜索、元素识别分别展开讲也会把部分核心代码片段放出来欢迎持续关注。
延伸阅读

更多相关文章

2026/10/5 1:22:13

PX4开发环境搭建:Ubuntu 18.04+QGC+Qt Creator实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 1:17:12

Matlab 2023b安装配置MOSEK 10.1.25:从License到路径的完整避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 1:17:12

智能家居开源硬件项目查找指南:渠道筛选与学习路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 5:22:22

PyQt5+OpenCV实现暗通道先验图像去雾:从原理到桌面工具

简介:这是一份基于PyQt5与OpenCV的暗通道先验图像去雾系统毕业设计源码包,面向计算机、人工智能、电子信息等专业学习者,可用于课程实践、毕业设计或科研参考。系统以经典暗通道先验理论为核心,借助NumPy与OpenCV完成透射率估计、…

2026/10/5 5:22:22

STM32软件模拟IIC驱动AHT21B温湿度传感器实战

前阵子有个做环境监控的活儿,需要在一款基于 STM32 的主控板上加一路温湿度采集,传感器选来选去,最后定了 AHT21B。这个芯片精度不错,成本也低,通信接口是 IIC。不过实际用的时候,板子上的两个硬件 I2C 外设…

2026/10/5 5:22:22

暗通道先验去雾实战:PyQt5+OpenCV桌面系统开发与参数调优

简介:这是一套基于PyQt5与OpenCV的暗通道先验图像去雾系统毕业设计项目,面向计算机视觉、人工智能及电子信息工程等专业的学生与研究者,可作为课程实践、毕业设计或科研项目的参考方案。项目以Python为核心,结合numpy数值计算库&a…

2026/10/5 5:22:22

大模型API Token成本计算实战:Python脚本与优化指南

1. 从一次账单异常说起:为什么Token成本值得单独算一笔账上个月帮一个朋友看他团队的API账单,发现一个很有意思的现象:他们做的是一个文档摘要类的小工具,日活不高,请求量也不算夸张,但月度费用比预期高出了…

2026/10/5 5:22:22

MCGS触摸屏Modbus批量读取优化:从原理到配置,解决画面刷新慢

遇到过这样一个现场:一台MCGS触摸屏通过RS485接了一台变频器,画面上放了电压、电流、频率、母线电压、温度等20多路实时数据,运行后数值刷新总慢半拍,切换页面明显卡顿。现场工程师怀疑触摸屏性能不行,换了个更贵的型号…

2026/10/5 5:17:21

Linux下迈德威视工业相机接入OpenCV的完整指南

做机器视觉项目,最绕不开的一环就是把工业相机“喂”给图像处理库。我最近在Linux环境下做一个视觉检测的方案,相机用的是迈德威视(MindVision),图像处理这边选OpenCV,说实话这条链路不算难,但坑…

2026/10/4 0:01:02

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/4 0:01:02

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/4 1:01:05

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑