news 2026/9/28 1:07:01

PyCircuit 6:用Python代码写电路,自动生成原理图与PCB

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PyCircuit 6:用Python代码写电路,自动生成原理图与PCB

我要先坦白一件事:PyCircuit 这个项目最初的起点,真的不是为了做一个面向大众的开源硬件开发框架。它的动机特别小,小到说出来自己都嫌丢人——我就是想在画转接板时,省掉在EDA里反复拖拽连线的十五分钟。结果大家都看见了,为了那瓶醋,我把这盘饺子重新包了一遍,而且一包就是六个大版本。PyCircuit 6 是我最近整理完的大版本,它做的事总结起来就一句:你可以用Python代码描述一块电路板,然后让工具自动产出原理图、PCB制造文件、物料清单,连着引脚映射的头文件和硬件回归测试脚本一起丢给你。这篇复盘就围绕这盘饺子展开,说说醋是什么、饺子皮怎么擀、馅儿怎么调,以及包到第六版时踩过的几个真坑。

1. 那瓶"醋":一个本该十五分钟做完的转接板

1.1 需求从哪来

去年我在给一块FPGA板子做原型验证。板子本身没问题,但每验证一种新的引脚分配方案,就要换一块转接板。FPGA的好处是GPIO能任意映射,坏处是映射方案一变,物理连线的意义就全变了。

当时手头这个转接板特别不起眼:一片电平转换芯片,几个退耦电容,两排排针,外加几个测试点。按说放任何EDA里,熟练工十分钟十五分钟就能画完。但我那段时间处在一种"上午定方案、下午改方案、晚上再改回来"的状态里,一天下来光改连线就要折腾四五个来回。每改一次,不是拉线拉到头大,而是改完以后还要核对网络表、跑一遍DRC、重新导BOM,整套流程四十多分钟起步。一周累计下去,我发现时间全花在了"确认设计没被我自己改坏"上面,而不是花在思考"这个设计到底好不好"。

这个场景就是那瓶醋。它太具体了,具体到我没法理直气壮地找一个现成工具来买。它不是一份需求文档,而是一种爆发在重复劳动里的烦躁——而这种烦躁,恰好是所有工具作者共同的起点。

1.2 真正让我动手的是一个"别扭"

光说"嫌麻烦"其实还差点火候。让我真正决定要自己折腾的,是一种职业上的别扭。

我写软件的时候,代码就是设计本身。我可以提交一个PR,别人能diff,能在注释里讨论"为什么这么写"。可一转到硬件,设计意图散落在一张图片、一堆坐标和一堆鼠标点击记录里。你想让同行审一下"我为什么把这两个去耦电容放在这里",他只能看着你的原理图猜。

我想要的是那种"硬件也有源码"的感觉:元件放哪里、线连到哪里、为什么这样连,都能用文本记录下来。板子的图纸也好、Gerber也好,都应该是从这份设计描述"编译"出来的产物,而不是设计的起点。这个念头一旦冒出来,就再也回不去了。

1.3 触发点:先拿JSON试水

动手第一步特别土。我没打算写框架,就写了一个Python脚本,读一个JSON配置,把里面列的电源网络、信号网络、接口引线整理成一份KiCad能导入的netlist。

第一次跑通的时候,说实话我盯着终端看了好几秒,没缓过神。那感觉不是"省了几分钟"的爽,而是"我突然可以像开会讨论架构一样讨论一块电路板"的新鲜感。我在JSON里加一个字段,重新生成,再打开EDA,连线已经按新定义排好了。

从那一刻起,我就知道这瓶醋已经不止值一盘饺子了。它值好几屉。

2. 盘饺子之前:为什么现有Python方案不够吃

2.1 当时能选的东西,我基本都试了一遍

决定要搞"用代码描述硬件"之后,我首先想到的是看看开源社区已经有什么。毕竟自己造轮子之前,至少得先确认轮子真的不合脚。

那一圈试下来,结论有点微妙。SKiDL,老牌Python库,用代码描述电路拓扑的思路和我想的很接近。但它出生的年代早,底层抽象偏"网表流",它帮你生成的东西更接近"给EDA导入的连接关系",而不是一份人愿意直接阅读的原理图。它生成的图,排版基本靠运气,你想让一块电源模块和一块主控模块在纸上间隔拉开一点,它根本不管。做原型验证可以,做团队项目的交付物很吃力。

schemdraw,画原理图是好手,出图漂亮,教科书级。但它定位就是"画示意图",它根本不知道什么是电源域,不知道两个Pin接在一起会不会冲突。你让它画一个桥式整流电路没问题,让它告诉你"这个电容接错管脚了",它一脸无辜。

各家EDA的脚本接口我也没少折腾。JLCEDA有脚本能力,KiCad有Python插件框架,但它们的本质是"操作一个设计文件"。你要生成点什么还得先创建一块板子、创建封装、连接网络。它们能自动化,但"设计意图"还是得靠人一点点喂进去,并没有变成一个可讨论、可复用的东西。

2.2 共同的问题:它们都把"图"当本体,把"逻辑"当配菜

这几种方案有个共同盲区:原理图是它们的核心数据和唯一交付物。可我一直在想,硬件开发里真正该被当作"本体"的,是那张图吗?

不是。图是给人看的解释,而真正的本体是连接关系和约束。连接关系是理性的,可以被机器检查;图是感性的,是给人做心理按摩的。SKiDL把连接关系做了,但没做好人的体验;schemdraw把人的体验做了,但没做过连接关系的检查。它们都假设"画出一张像样的图"是目的,而我觉得"图"只是附带福利。

这个认知差异看起来不大,但会彻底影响架构:如果你把"图"当本体,那么一切工作都围绕排版、坐标、图框、标号、页间连接符来转;如果你把"连接与约束"当本体,那么一切能力都围绕检查、推导、复用、验证来转,出图只是其中一个不算核心的后端。我的选择是后者。

2.3 自研的边界:只做"编译前端",不做EDA

确定了方向,也就有了边界意识。我不会傻到去做一个完整EDA,那是无底洞。PCB布线、阻抗计算、仿真求解、元件库维护,任何一项都够一个团队吃十年。

我能做、也值得做的,恰恰是中间这一层:把Python里的硬件描述编译成结构化数据,然后让不同的后端渲染器把这些数据变成原理图、网表、BOM、固件头文件。这套模式和我熟悉的编译器太像了:前端解析代码、生成中间表示,后端各自消费中间表示。一个IR,N个后端,各不干涉。

这个定位后来救了我很多次。因为边界清楚,所以我每个版本都在提醒自己:渲染器与核心逻辑解耦,凡是跟"出图"相关的功能都扔到后端去,凡是跟"电路对不对"相关的功能都留在核心。谁也别污染谁。

3. PyCircuit 6 的核心抽象:把电路当作带约束的图

3.1 三个类撑起整个建模

如果你要用代码描述硬件,最怕的是抽象不完整。我试过好几版,最后沉淀下来只需要三个东西:元件、管脚、网络。

元件描述"板上有什么",它携带符号、封装、数据手册引用,可能还有每一个管脚的行为描述。管脚描述"电从哪进、从哪出",它有方向(输入、输出、双向、电源、接地),有电气类型(数字、模拟、功率、地)。网络描述"谁跟谁接在一起",它把一组管脚连成一个电气等价点。

定义逻辑上很简单,但细节决定成败。比如电源管脚的命名,很多芯片的数据手册上同一个地一会儿叫GND,一会儿叫PGND,一会儿又叫AGND;封装库里它可能叫EP,也就是exposed pad。如果你不在一开始就把"裸露焊盘也是接地Net"这件事建模,后面DRC会把你折腾到怀疑人生。

我的第一版实现就吃了这个亏:只建了Net,没建Pin的电气类型,结果两个输出Pin接到同一个Net上,生成器一声不吭,板子打出来烙铁都焊完了,上电才看到问题。所以后来我把"角色"和"名字"彻底分开,名字可以随便叫,角色必须唯一。

3.2 层级复用:把子电路当成函数来调用

第二个让PyCircuit真正变得好用的设计,是层级复用。

硬件工程师都有体会:一个系统里反复出现的往往不是大模块,而是"电源去耦加状态LED加串联电阻加去耦电容"这种小组合。在传统EDA里,你得复制粘贴,然后祈祷后来不要改其中某个封装。在PyCircuit里,这种组合就是一个普通函数。

比如定义一个"为某个电源域加一份去耦并留一个测试点"的子电路:

def power_clean(domain, refdes_prefix): net_3v3 = Net(domain) cap1 = Component('cap_100n', 'C0603', refdes=f'{refdes_prefix}1') cap2 = Component('cap_1u', 'C0603', refdes=f'{refdes_prefix}2') tp = Component('testpoint', 'TP_1mm', refdes=f'{refdes_prefix}3') net_3v3.connect(cap1.pin(1), cap2.pin(1), tp.pin(1)) net_gnd = Net('GND') net_gnd.connect(cap1.pin(2), cap2.pin(2), tp.pin(2)) return net_3v3, net_gnd

然后主板上有几路独立的3.3V域,就调用几次。参数化能力意味着同一个函数能衍生出不同容值的组合、不同位数的总线。这个感觉太像写代码了,而且它保证了"每一路电源域的做法完全一致",不会出现手工画板时"这块忘了加100nF电容"的经典事故。

3.3 把DRC前移:连接检查应该在代码编写时

传统硬件的DRC是在生成PCB之后才跑的,晚到不能再晚。PyCircuit的做法是让检查跟着图建立的过程一起跑:每次把一个管脚挂到Net上,都要回答三个问题——这个管脚允许挂在这样一个名字的Net上吗?这个Net上已经有没有东西在驱动它?会不会有两个输出直接打架?

我不是一次性把DRC做完的,是分版本一点点堆起来的。最开始只查管脚方向冲突,后来加了电源域一致性检查,再后来加了"同一封装内两个功能管脚被接错"的检查。每检查一种,我手头的一个老项目就会多暴露几个隐藏问题,这让我越来越相信硬件更应该被当作代码来管理。

有个例子印象很深:我在描述一块电池充电板的时候,把保护芯片的一个管脚方向标错了方向。在PyCircuit里,实例化完成的那一刻,警告就弹出来了,提示"该Pin声明为Output,但你把它连到了一个有源的充电器输出上"。这是任何原理图工具都不会主动告诉你的。

3.4 多后端生成:一套描述,五处产出

到PyCircuit 6,一个编译好的电路模型会同时喂给几个后端:

后端产出主要用途
原理图渲染SVG / PDF,带页间网络标号人读、评审、归档
PCB生成KiCad工程、Gerber、钻孔文件打样、装配
网表后端KiCad / LTspice 兼容网表仿真、EDA导入
固件后端C/HAL头文件、引脚宏定义软件工程师直接使用
测试后端每个关键Net的短路/开路测试桩工厂返修、上电自检

这个结构明显是跟编译器学的:核心只维护语义,后端各自负责消费。好处是后面加任何新后端都不需要动核心数据结构;坏处也不是没有——后端多了之后,必须有一套强力的输出测试兜底,否则一个改动会让五个产出的格式同时漂移。这也是v6把CI放在很高优先级的原因。

4. 从v1到v6:重新包这盘饺子的六个大版本

4.1 v1:一个能生成netlist的脚本

最开始的版本根本配不上"框架"两个字。它就是一个一百多行的Python脚本,输入JSON,输出netlist。用户界面不存在的,注释全靠我临时加,能用的场景只有我自己那块转接板。

但就是这样一个简陋玩意,让我把"板子"从CAD文件的角度换成了"板子是从数据推导出来的"的角度。我发现netlist一旦能生成,后面很多事都只是时间问题。

4.2 v2:引入元件库和封装库

JSON写多了是地狱。很快我发现,板级项目里真正需要反复喂的就是元件。于是v2引入了统一的元件描述格式,用YAML维护了一个五脏俱全的小库。

这一步现在看很平淡,但当时救了我一个大跟头:不同芯片厂商对同一个功能管脚的命名能差出十万八千里。比如同样是时钟输出管脚,有的叫CLKOUT,有的叫MOUT,有的叫CO。如果不把这些语义统一到Pin的电气角色上,所有下游检查都是空谈。元件库存在的意义是让"物理管脚"和"电气角色"分离,后面所有智能化分析都从这里受益。

4.3 v3:原理图渲染器

逼着我把原理图渲染器写出来的原因有点尴尬:我一个人用脚本跑电路没问题,但和外协、和同事沟通时,没有人愿意看JSON。大家都要看"图",一张放在桌上能指指点点的纸。

v3做的事就是从netlist反推原理图布局。说实话,最开始的输出丑得没法看,但它解决了"有没有"的问题,再慢慢解决"好不好看"的问题。我后来用了一种简单但有效的启发式:先按信号流方向给元件分层(输入到处理再到输出),再在同一层内做横向排列,电源和地统一用电源轨拉出。这个规则本质上就是把工程师画电路图的习惯翻译成了布局算法。

4.4 v4:PCB交互导出

v4是个分水岭。它让PyCircuit不再停留在"纯生成"的层面,而是能跟真实EDA工作流互动。做法是通过KiCad的Python接口,把生成的网表和初步布局推送到pcbnew里,再由人在里面做精细的布线微调。

这个"半自动"设计很重要。我发现全自动布局在原理图层面也许够用,但一到PCB层面,电磁兼容、走线宽度、热问题、机械约束全都不是"纯逻辑"能解决的。与其花五年去训练一整套物理优化器,不如做一个聪明的自动化:把元件按逻辑分组预先摆放,让人来拖最后几毫米。

4.5 v5:从网表到固件框架

v5有点野心了。既然电路连接关系已经完全存在于数据结构里,那么板子上的芯片从哪个GPIO进来、哪个GPIO出去,软件工程师是不是也能直接吃到现成的?

于是v5加了一个固件后端:给定SoC的管脚映射和Net名称,自动生成一份C头文件,里面把所有复用功能、GPIO初始化需要的宏、外部中断映射的IO号全部定义好。从此硬件和软件的接口不再是"N页的引脚定义PDF",而是一个可以被diff、被review、被注释的文件。

这个版本彻底改变了我的开发方式。以前硬件定型后我要花半天给固件同事解释引脚,现在直接把生成的头文件发给对方,他include进去就能开始写驱动。

4.6 v6:回归测试与CI

如果项目只有我自己用,v5就是终点。但v6我把它当成了一个"成熟工具该有的样子"来做,核心是回归测试和持续集成。

我在项目里建了一组金标准板卡,这些板卡用PyCircuit描述,对应的输出文件(原理图SVG、网表、头文件、BOM)经过人工确认后,被冻成快照。之后任何代码改动,CI都会重新生成这些板卡,并和快照做diff。一旦某个Net顺序变了、BOM里多了个空格,CI就报警。

它还支持在CI里跑完整DRC,这样每次commit都会触发"描述语法检查加电气规则检查加输出快照比对"三层验证。这一步让PyCircuit从"一个人的脚本"变成了"可以放心交给别人用"的工具链。

5. 实测一把:用PyCircuit 6从文字到贴片

5.1 定一个具体目标

纸上谈兵容易,我们来看一个真实用的例子。最近我手头做一个环境监测小板:主控用STM32G031G8,外接两颗状态LED、一颗I2C温湿度传感器、一颗电压检测用的分压电阻网络,外加一个2.54mm的调试排针,电源用USB转串口模块的3.3V直接供给。

这个板子复杂度中等,但五脏俱全:有电源域、有时钟复位、有通信接口、有低速IO,还有典型的"上电顺序"问题。

5.2 用PyCircuit描述这块板子

先定义电源和地,然后逐个放元件、连Net。核心逻辑大致长这样:

from pycircuit6 import Board, Component, Net, PowerNet board = Board(title="env_sense_v1") vcc = PowerNet("3V3", voltage=3.3) gnd = PowerNet("GND", ref="AGND") # 电源去耦:按惯例给MCU的每个电源脚就近放100nF board.place(Component("c_mcu_100n", "C0603", refdes="C1")) board.net_cap_filter(vcc, gnd, cap=[100e-9, 1e-6]) # MCU mcu = Component("stm32g031g8", "UFQFPN28", refdes="U1") board.add(mcu) board.net("PB1_LED").connect(mcu.pin("PB1")) board.net("PA9_UART_TX").connect(mcu.pin("PA9")) # LED 和限流电阻 led1 = Component("led_0402", "LED_0402", refdes="D1") r1 = Component("res_1k", "R0603", refdes="R1") board.series(vcc, r1.pin(1), led1.pin(1)) board.net("PB1_LED").connect(led1.pin(2)) # I2C sensor = Component("sht40", "DFN-4", refdes="U2") board.net("I2C_SCL").connect(mcu.pin("PB6"), sensor.pin(2)) board.net("I2C_SDA").connect(mcu.pin("PB7"), sensor.pin(3))

这段代码看着很短,但每个函数背后都做了一堆隐性工作:series帮我们把两个Pin串成一个串联子电路并检查方向,net_cap_filter自动生成去耦电容并把两端正确挂到电源域。生成的模型里已经有完整的分层结构和DRC信息。

5.3 运行、验证、渲染

写完之后,在项目终端里敲一条命令:

pycircuit build env_sense.py --emit pdf --emit kicad --emit header --emit bom

几秒钟后,输出目录里出现了原理图PDF、KiCad工程、bom.csv和stm32_hal_pins.h。我又特意跑了一条:

pycircuit check env_sense.py

它返回了三条警告,其中一条是"MCU的PF2管脚被用作了复位引脚,却在Net BRIDGE_ON上",确实是我在描述阶段漏掉的。这种错误以前要在打板回来用万用表测半天才能发现,现在生成阶段就拦住了。

5.4 产出物清单

一批文件直接可用:

文件格式用途
env_sense_schematic.pdfPDF评审和归档
env_sense.kicad_sch / .kicad_pcbKiCad工程文件继续精调或直接生产
env_sense/gerber/*.gbrGerber找工厂打样
env_sense/bom.csvCSV采购与贴片
env_sense/stm32_hal_pins.hC头文件固件工程师直接include

我拿Gerber去打了样,板子回来焊好后两块板全部正常工作。两块板都一次点亮这件事,说实话比我预期的要顺利,因为以往这种中密度的板子,第一次打样总会有几个小毛病要飞线。

6. 真坑记录:硬件生成器容易翻车的几个地方

6.1 管脚命名是个无底洞

做硬件描述框架,最想不到的坑是管脚命名。

芯片官方给的名字,和EDA厂商库里用的名字,和封装库里的物理引脚编号,常常不是一套系统。比如某个MCU的SWD接口,官方叫SWDIO,封装库里叫PA13,你的代码再用信号语义叫"PROGRAM_DATA"。如果元素库里没有把这三层关系建立好,任何下游检查都会变成猜谜。

我在PyCircuit里给出的答案是:元件库的每个Pin至少要带上三份元数据——物理编号、符号名、电气角色。所有规则检查都优先信任"电气角色"这一层,其余的只是展示面。你不这样分层设计,后面换封装批次或者芯片改版时一定会炸。

6.2 逻辑连通的电路,物理上可能完全走不通

这是所有"代码生成硬件"方案最容易被高估的地方。软件里,逻辑正确基本等于结果正确;硬件不是。

举个简单例子:代码里MCU的3V3管脚接了一个100nF去耦电容然后接地。逻辑完全正确。但如果布局时那个电容被放在离芯片5厘米远的地方,这个"连接"在电磁上等于没接。电源完整性是物理问题,不是逻辑问题。生成器可以在原理图层面保证你"连对了",却没法保证你"放好了"。

我的应对是"约束提示"机制。在描述阶段,可以直接给电源管脚标记decoupling_nearest提示给布局后端,让它在自动摆放时优先把去耦电容贴近对应电源管脚。它不是一个物理优化器,但至少是一个"把经验写进代码"的通道。

6.3 渲染器的可读性要单独投入

还有,别以为原理图渲染器只是把元件画出来。真正有价值的可读性来自布局:谁在左、谁在右、电源如何跨页、信号流方向是否一致。

我一开始天真地按元件名称字母序排,出的图完全没法看。后来把工程经验翻译成三条启发式:输入放左、输出放右、电源轨统一上下。每条规则都是我对着自己手工画的图总结出来的。这条路上没有捷径,只有不停地拿真实项目当测试集,用工程师的眼睛做标定。

6.4 别把生成器做成黑盒

最后一个坑,是工具边界本身。我见过太多自动生成工具,输出像魔法:你改一行代码,结果文件变了,但没人能解释为什么。

PyCircuit从v3开始就坚持一个原则:每一个生成文件都必须"可被审计"。网表可以按图索骥地查,PDF可以找到每个元件对应的生成逻辑,头文件里的每行宏定义都能追溯到那句电路描述。有一次我在DRC里加了一条规则后依赖顺序没调好,同一个Net的枚举顺序全变了,CI里快照diff立刻刷了几百行。如果没保留这个"可解释性",用户根本不知道是哪里出了问题。

这个原则比任何花哨功能都重要。因为它意味着:你的工具出错时,团队还能降级回去干活;如果工具是黑盒,一出错就只能等作者修,整个交付节奏都会被打断。

如果让我说这六年最值的一点,其实是"设计即源码、图纸即产物"这个工作方式带来的正反馈循环:改动成本越低,就越敢改;越敢改,设计质量就越好。如果你也想从"画板"切换到"写板",我的建议很简单:别一上来就追求生成PCB。先把每天都要做的接口转接、电源去耦、MCU核心板这几个子电路封装成可复用组件,每两周多封装一个,半年之后你会发现自己的"饺子馅"已经不知不觉攒了一大盆。到那时再回头审视要不要全套自动生成,你会比任何人都清楚这条路到底适不适合自己。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 1:06:23

别瞎折腾:图解WordPress卖产品全流程,7步搞定从建站到收款

别瞎折腾:图解WordPress卖产品全流程,7步搞定从建站到收款 网站做好了没人访问?别急着骂算法,先看看你的货架搭对没。很多老板花几万块做完官网,首页美轮美奂,但客户进来转一圈就走了,转化率惨不忍睹。问题往往出在“卖产品”的逻辑上,而不是SEO排名。今天不聊虚的,直接上 图解步骤…

作者头像 李华
网站建设 2026/9/28 1:05:43

新手入门避坑:3种h5个人博客网站模板成本拆解

新手入门避坑:3种h5个人博客网站模板成本拆解 改个需求建站公司拖一周,这种憋屈事我见得太多了。很多新手入门做 h5 个人博客网站模板 时,总以为找个现成模板就能省事,结果陷入“改代码找外包、改样式加钱”的无底洞。…

作者头像 李华
网站建设 2026/9/28 1:05:42

南充网站建设网站避坑指南:搞懂域名服务器再谈建站报价

南充网站建设网站避坑指南:搞懂域名服务器再谈建站报价 域名解析报错404,服务器连接超时,SSL证书安装失败……很多南充的朋友在找南充网站建设网站服务商时,第一反应是问“多少钱”,但往往忽略了一个致命前提:你根本搞不懂域名和服务器是怎么配合工作的。…

作者头像 李华
网站建设 2026/9/28 1:05:34

头颅侧位片蝶鞍语义分割数据集构建与训练指南

简介:本资源是面向医学影像AI研究者与深度学习初学者的头颅侧位片蝶鞍区域语义分割专用数据集,聚焦内分泌疾病辅助诊断场景,解决蝶鞍形态异常识别中高质量标注数据稀缺的痛点。数据包共1558个文件,含779张标注用头颅侧位X光JPG图像…

作者头像 李华
网站建设 2026/9/28 1:05:26

做网站编辑好还是新媒体编辑怎么选?3个维度拆解真相

做网站编辑好还是新媒体编辑怎么选?3个维度拆解真相 改个需求建站公司拖一周,这种憋屈谁受得了?很多刚入行的新人或者想转行的老鸟,站在十字路口纠结:是做偏技术的网站编辑,还是做偏内容的自媒体编辑?这俩岗位听起来都沾边,但干起来完全是两码事。别被那些虚头巴脑的招聘JD忽悠了,咱们得把底层逻辑扒开看,才能…

作者头像 李华
网站建设 2026/9/28 1:05:18

安庆网站设计避坑指南:搞定域名服务器与性能优化

安庆网站设计避坑指南:搞定域名服务器与性能优化 域名买对了没?服务器配够了没?这俩要是搞砸了,安庆网站设计做得再漂亮也是白搭。 很多新手一上来就盯着页面看,结果上线后打开像蜗牛,客户全跑了。其实, 域名注册 和 服务器部署 才是地基,地基不稳,房子必塌。…

作者头像 李华