一、开篇:为什么「AD 二次开发 + DeepSeek Coder」是 PCB 设计的革命性突破
在传统的 PCB 设计流程中,工程师们花费大量时间在重复性操作上:手动标注数百个元件标号、逐层导出 Gerber 文件、反复检查设计规则……这些机械性工作占据了宝贵的设计时间,而真正的电路创新与优化却被挤压到角落。当AD的工业级 PCB 设计能力,遇上DeepSeek Coder的智能代码生成能力,我们正在见证一场设计范式的根本性变革。
1.1 传统 PCB 设计流程的痛点分析
| 痛点场景 | 传统手动操作 | 耗时估算 | 错误率 | 对设计创新的影响 |
|---|---|---|---|---|
| 原理图注释 | 逐个修改 R?、C? 为连续编号 | 30-60分钟/百元件 | 5-10% | 消耗初期设计精力 |
| 设计规则检查 | 逐项核对间距、线宽等规则 | 20-40分钟/次 | 3-8% | 打断设计连续性 |
| 生产文件输出 | 重复设置 Gerber、钻孔、BOM 等 | 45-90分钟/套 | 8-15% | 增加交付前压力 |
| 元件库管理 | 手动创建/修改封装库 | 数小时/复杂器件 | 10-20% | 延迟项目启动时间 |
这些痛点并非技术难题,而是工作流效率瓶颈。资深工程师都清楚,PCB 设计的核心价值在于电路拓扑优化、信号完整性分析和 EMI 控制,而非鼠标点击次数。
1.2 智能脚本助手的革命性价值
自然语言 → 可执行脚本 → 自动化设计,这个三级跳工作流将彻底改变 PCB 工程师的工作方式:
- 效率指数级提升:原本需要数小时的手动操作,通过脚本可在几分钟内完成
- 错误率趋近于零:脚本执行消除了人为疏忽,确保每次操作的一致性
- 知识沉淀与复用:将个人经验封装为脚本,形成团队知识资产
- 设计质量标准化:通过脚本强制执行最佳实践和设计规范
更重要的是,DeepSeek Coder 的加入让脚本开发不再是程序员的专利。即使没有编程背景的 PCB 工程师,也能用自然语言描述需求,由 AI 生成可运行的 AD 脚本。
二、基础夯实篇:AD二次开发全体系零基础认知
2.1 AD 二次开发的核心价值:告别重复劳动
AD作为一套完整的电子产品开发系统,其强大之处不仅在于丰富的设计功能,更在于开放的二次开发接口
。通过二次开发,我们可以:
| 开发目标 | 具体实现 | 效率提升倍数 | 应用场景 |
|---|---|---|---|
| 流程自动化 | 一键完成标注→检查→输出全流程 | 5-10倍 | 每个设计项目 |
| 质量标准化 | 自动执行设计规则检查与修复 | 3-5倍 | 团队协作项目 |
| 数据提取 | 自动计算铺铜面积、寄生参数等 | 10-20倍 | 设计分析与报告 |
| 定制功能 | 开发专用工具满足特殊需求 | 无法量化 | 特定行业应用 |
二次开发的本质是将设计经验转化为可重复执行的数字指令。正如搜索结果所示,AD 二次开发可以基于 SDK 的 C#、C++ 开发,也可以使用 Delphi Script 脚本开发。对于大多数 PCB 工程师,脚本开发是更实用、更快捷的入门选择。
2.2 AD 原生支持的脚本语言全维度对比
虽然 AD 使用 Delphi 开发,对 Delphi Script 支持最好,但随着技术发展,Python 已成为更主流的选择。以下是两种语言的详细对比:
| 对比维度 | Delphi Script(传统方案) | Python(现代方案) | 推荐选择 |
|---|---|---|---|
| 学习曲线 | 需要学习 Pascal 语法,资料较少 | 语法简洁,资源丰富,AI 支持好 | ✅ Python |
| 社区生态 | 局限于 AD 社区,更新缓慢 | 全球开发者社区,库丰富 | ✅ Python |
| AI 支持度 | DeepSeek Coder 训练数据较少 | 大量 Python 代码训练数据 | ✅ Python |
| 功能完整性 | 直接调用 AD 内部 API,功能全面 | 通过 COM 接口调用,功能足够 | 各有优势 |
| 开发效率 | 需要编译,调试较复杂 | 解释执行,实时调试 | ✅ Python |
| 未来趋势 | 维护性下降,新功能支持慢 | 持续更新,生态活跃 | ✅ Python |
关键结论:对于 AI 辅助开发场景,Python 是更优选择。DeepSeek Coder 在 Python 代码生成方面表现更出色,且 Python 的简洁语法更适合自然语言转代码。
2.3 永久开启 AD 的 Python 二次开发接口(3 步完成)
AD 默认不显示 Python 脚本支持,需要手动开启。以下是详细步骤:
步骤 1:启用 Python 脚本支持
1. 打开 AD → 点击右上角齿轮图标(设置) 2. 选择 System → General 3. 勾选 "Enable Python Scripting Support" 4. 重启 AD 使设置生效步骤 2:配置 Python 环境
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| Python 版本 | 3.7-3.9 | AD 对 3.10+ 支持可能不稳定 |
| 安装路径 | C:\Python37 | 避免中文和空格路径 |
| 环境变量 | 自动添加 | 安装时勾选 "Add to PATH" |
| 必要库 | win32com, pywin32 | 用于 COM 接口调用 |
步骤 3:验证安装
创建测试脚本test_connection.py:
python
import win32com.client # 连接 AD 应用程序 try: ad = win32com.client.Dispatch("Altium.Application") print("✓ AD 连接成功!版本:", ad.Version) # 获取当前文档 doc = ad.GetCurrentDocument() if doc: print("✓ 当前文档:", doc.DocumentName) else: print("⚠ 未打开任何文档") except Exception as e: print("✗ 连接失败:", str(e))运行成功输出表示环境配置正确。
2.4 AD Python 二次开发的核心依赖与环境配置
为确保零报错开发,需要完整配置以下环境:
| 组件 | 版本要求 | 安装命令 | 功能说明 |
|---|---|---|---|
| Python | 3.7.9 | 官网下载安装包 | 脚本运行环境 |
| pywin32 | 300+ | pip install pywin32 | Windows COM 接口支持 |
| AD 版本 | 20.0+ | 官方安装 | 提供 COM 接口 |
| 代码编辑器 | VS Code | 官网下载 | 脚本编写与调试 |
| DeepSeek Coder | 6.7B+ | 按指南部署 | AI 代码生成 |
环境验证脚本:
python
# environment_check.py import sys import pkg_resources required_packages = { 'pywin32': '300', 'pythoncom': '300' } print("="*50) print("AD Python 二次开发环境检查") print("="*50) # 检查 Python 版本 print(f"Python 版本: {sys.version}") if sys.version_info < (3, 7): print("⚠ 警告: Python 版本低于 3.7,建议升级") # 检查必要包 print("\n检查必要包安装:") all_ok = True for package, min_version in required_packages.items(): try: dist = pkg_resources.get_distribution(package) print(f" ✓ {package}: {dist.version}") except pkg_resources.DistributionNotFound: print(f" ✗ {package}: 未安装") all_ok = False # 检查 AD 连接 print("\n检查 AD 连接:") try: import win32com.client ad = win32com.client.Dispatch("Altium.Application") print(f" ✓ AD 连接成功: {ad.Version}") # 测试基本功能 doc = ad.GetCurrentDocument() print(f" ✓ 文档访问: {'成功' if doc else '无打开文档'}") except Exception as e: print(f" ✗ AD 连接失败: {e}") all_ok = False print("\n" + "="*50) if all_ok: print("✅ 环境检查通过!可以开始 AD 二次开发") else: print("❌ 环境检查未通过,请根据提示修复")2.5 AD 二次开发的「圣经」:官方 API 文档的查阅与使用技巧
AD 的 API 文档是二次开发的基石,但官方文档分散且不易查找。以下是高效使用指南:
文档来源与特点:
| 文档类型 | 位置 | 内容特点 | 适用场景 |
|---|---|---|---|
| COM 接口文档 | AD安装目录\System\Help\ | 最权威,但格式陈旧 | 查找精确接口定义 |
| 脚本示例库 | GitHub: Altium-API-Scripts | 实用案例,可直接运行 | 学习具体功能实现 |
| 社区脚本 | 各大电子论坛、博客 | 实战经验,有中文解释 | 解决具体问题 |
| 对象模型图 | 自行绘制(下文提供) | 直观展示对象关系 | 理解整体架构 |
高效查阅技巧:
- 按对象层次查找:AD 采用层次化对象模型,从 Application → Document → Object 逐层查找
- 使用接口探查工具:Python 的
win32com.client支持动态探查python
import win32com.client ad = win32com.client.Dispatch("Altium.Application") # 查看所有方法 for attr in dir(ad): if not attr.startswith('_'): print(attr) # 查看具体方法的帮助 help(ad.GetCurrentDocument) - 从示例反推:找到类似功能的示例脚本,分析其 API 使用方式
2.6 AD 核心对象模型:操控 AD 的「积木块」
理解 AD 的对象模型是二次开发的关键。以下是简化的核心对象层级:
Application (顶层对象) ├── Documents (文档集合) │ ├── SchematicDocument (原理图文档) │ │ ├── Components (元件集合) │ │ │ ├── Component (单个元件) │ │ │ │ ├── Designator (标号) │ │ │ │ ├── Parameters (参数) │ │ │ │ └── Pins (引脚) │ │ │ └── ... │ │ ├── Nets (网络集合) │ │ └── Sheets (图纸集合) │ └── PCBDocument (PCB文档) │ ├── Board (板对象) │ │ ├── Layers (层集合) │ │ ├── Components (PCB元件) │ │ ├── Tracks (走线) │ │ ├── Vias (过孔) │ │ ├── Pads (焊盘) │ │ └── Polygons (铺铜) │ └── Rules (设计规则) └── Preferences (系统设置)关键对象详解表:
| 对象名 | 访问方式 | 常用属性/方法 | 典型用途 |
|---|---|---|---|
| Application | win32com.client.Dispatch("Altium.Application") | Version,GetCurrentDocument(),RunProcess() | 全局控制,运行内部命令 |
| SchematicDocument | ad.GetCurrentDocument()(当原理图激活时) | Components,Nets,AddComponent() | 原理图操作,元件管理 |
| PCBDocument | ad.GetCurrentDocument()(当PCB激活时) | Board,Rules,AddTrack() | PCB布局布线操作 |
| Component | sch_doc.Components.Item(index) | Designator,Footprint,Location | 元件属性读取与修改 |
| Board | pcb_doc.Board | Layers,Width,Height | 板框与层管理 |
| Track | board.Tracks | Width,Layer,Start,End | 走线创建与编辑 |
2.7 AD 脚本开发必备基础:坐标单位转换 / 消息输出 / 错误处理
2.7.1 坐标单位转换(核心难点)
AD 内部使用纳米(nm)作为基本单位,而工程师习惯使用毫米(mm)或密尔(mil)。单位转换是脚本开发的第一道坎。
转换关系表:
| 单位 | 与纳米关系 | 转换公式 | 精度说明 |
|---|---|---|---|
| 纳米 (nm) | 1 nm = 1 nm | 基准单位 | AD 内部存储单位 |
| 毫米 (mm) | 1 mm = 1,000,000 nm | mm = nm / 1e6 | 机械设计常用 |
| 密尔 (mil) | 1 mil = 25,400 nm | mil = nm / 25400 | PCB 设计常用 |
| 英寸 (inch) | 1 inch = 25,400,000 nm | inch = nm / 2.54e7 | 英制单位 |
单位转换工具函数:
python
# unit_converter.py class UnitConverter: """AD 单位转换工具类""" # 定义转换常数 NM_PER_MM = 1000000 # 1毫米 = 1,000,000纳米 NM_PER_MIL = 25400 # 1密尔 = 25,400纳米 NM_PER_INCH = 25400000 # 1英寸 = 25,400,000纳米 @staticmethod def nm_to_mm(nm_value): """纳米转毫米""" return nm_value / UnitConverter.NM_PER_MM @staticmethod def mm_to_nm(mm_value): """毫米转纳米""" return int(mm_value * UnitConverter.NM_PER_MM) @staticmethod def nm_to_mil(nm_value): """纳米转密尔""" return nm_value / UnitConverter.NM_PER_MIL @staticmethod def mil_to_nm(mil_value): """密尔转纳米""" return int(mil_value * UnitConverter.NM_PER_MIL) @staticmethod def auto_convert(value, from_unit='nm', to_unit='mm'): """自动单位转换""" units = { 'nm': 1, 'mm': UnitConverter.NM_PER_MM, 'mil': UnitConverter.NM_PER_MIL, 'inch': UnitConverter.NM_PER_INCH } if from_unit not in units or to_unit not in units: raise ValueError(f"不支持的单位: {from_unit} -> {to_unit}") # 先转到纳米,再转到目标单位 nm_value = value * units[from_unit] return nm_value / units[to_unit] # 使用示例 if __name__ == "__main__": converter = UnitConverter() # 测试转换 print(f"1000 mil = {converter.mil_to_nm(1000)} nm") print(f"25.4 mm = {converter.mm_to_nm(25.4)} nm") print(f"自动转换: 100mil -> {converter.auto_convert(100, 'mil', 'mm'):.3f} mm")2.7.2 消息输出与调试
脚本执行时需要反馈信息,AD 提供了多种消息输出方式:
| 输出方式 | 代码示例 | 适用场景 | 优缺点 |
|---|---|---|---|
| 控制台打印 | print("消息") | 简单调试 | 只能在脚本编辑器查看 |
| AD 状态栏 | ad.SetStatusBarText("处理中...") | 长时间任务进度 | 用户可见,但不持久 |
| 消息对话框 | ad.ShowMessage("完成", "信息") | 重要通知 | 会打断用户操作 |
| 日志文件 | 写入文本文件 | 长期记录 | 需要文件操作 |
| 即时窗口 | ad.DebugPrint("调试信息") | 开发调试 | 需要开启调试模式 |
推荐的调试输出类:
python
# debug_logger.py import time import os class DebugLogger: """统一的调试输出类""" def __init__(self, log_file=None, enable_console=True): self.log_file = log_file self.enable_console = enable_console self.start_time = time.time() # 初始化日志文件 if log_file and not os.path.exists(os.path.dirname(log_file)): os.makedirs(os.path.dirname(log_file), exist_ok=True) def log(self, message, level="INFO"): """记录日志""" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") elapsed = time.time() - self.start_time log_entry = f"[{timestamp}] [{level}] [+{elapsed:.2f}s] {message}" # 控制台输出 if self.enable_console: print(log_entry) # 文件输出 if self.log_file: with open(self.log_file, 'a', encoding='utf-8') as f: f.write(log_entry + "\n") # AD 状态栏(简短消息) if len(message) < 50 and level == "INFO": try: import win32com.client ad = win32com.client.Dispatch("Altium.Application") ad.SetStatusBarText(message[:40]) except: pass def info(self, message): self.log(message, "INFO") def warning(self, message): self.log(message, "WARNING") def error(self, message): self.log(message, "ERROR") def section(self, title): """开始一个新章节""" self.info("=" * 60) self.info(f" {title} ") self.info("=" * 60) # 使用示例 logger = DebugLogger("ad_script.log") logger.section("开始执行原理图标注") logger.info("找到元件数量: 125") logger.warning("发现3个未标注元件") logger.error("连接AD失败")2.7.3 错误处理与容错机制
健壮的脚本必须有完善的错误处理:
python
# error_handler.py import traceback import sys class ScriptErrorHandler: """脚本错误处理器""" @staticmethod def safe_execute(func, *args, **kwargs): """安全执行函数,捕获所有异常""" try: return func(*args, **kwargs) except Exception as e: error_info = { 'function': func.__name__, 'error_type': type(e).__name__, 'error_msg': str(e), 'traceback': traceback.format_exc() } ScriptErrorHandler.log_error(error_info) return None @staticmethod def log_error(error_info): """记录错误信息""" error_msg = f""" ⚠️ 脚本执行错误 函数: {error_info['function']} 类型: {error_info['error_type']} 信息: {error_info['error_msg']} 追踪: {error_info['traceback']} """ print(error_msg) # 写入错误日志 with open("script_errors.log", "a", encoding="utf-8") as f: f.write("="*60 + "\n") f.write(error_msg + "\n") @staticmethod def retry_operation(operation, max_retries=3, delay=1): """重试机制""" for attempt in range(max_retries): try: return operation() except Exception as e: if attempt == max_retries - 1: raise print(f"操作失败,{delay}秒后重试 ({attempt+1}/{max_retries})...") time.sleep(delay) # 使用示例:安全的AD连接 def connect_to_ad(): """安全连接AD""" try: import win32com.client ad = win32com.client.Dispatch("Altium.Application") # 验证连接 if not ad: raise ConnectionError("无法创建AD应用对象") # 验证版本 version = ad.Version if not version: raise ConnectionError("无法获取AD版本") print(f"✅ 成功连接 AD {version}") return ad except Exception as e: ScriptErrorHandler.log_error({ 'function': 'connect_to_ad', 'error_type': type(e).__name__, 'error_msg': str(e), 'traceback': traceback.format_exc() }) return None # 在实际脚本中使用 ad = ScriptErrorHandler.safe_execute(connect_to_ad) if ad: # 继续执行 pass else: print("无法继续执行,请检查AD是否运行")2.8 第一个完整脚本:从原理图导出元件清单
让我们将以上所有知识整合,编写第一个实用脚本:
python
# schematic_bom_exporter.py """ 功能:从当前原理图导出元件清单(BOM) 作者:通过 DeepSeek Coder 生成 日期:2026-01-13 """ import win32com.client import csv import os from datetime import datetime # 导入自定义工具类 from unit_converter import UnitConverter from debug_logger import DebugLogger from error_handler import ScriptErrorHandler class SchematicBOMExporter: """原理图BOM导出器""" def __init__(self): self.logger = DebugLogger("bom_export.log") self.ad = None self.sch_doc = None def initialize(self): """初始化连接""" self.logger.section("BOM导出脚本初始化") # 连接AD self.ad = ScriptErrorHandler.safe_execute( win32com.client.Dispatch, "Altium.Application" ) if not self.ad: self.logger.error("无法连接Altium Designer") return False # 获取当前文档 self.sch_doc = self.ad.GetCurrentDocument() if not self.sch_doc: self.logger.error("未打开原理图文档") return False self.logger.info(f"连接成功: AD {self.ad.Version}") self.logger.info(f"原理图: {self.sch_doc.DocumentName}") return True def extract_components(self): """提取原理图中的所有元件""" self.logger.info("开始提取元件信息...") components = [] try: # 获取元件集合 comp_count = self.sch_doc.Components.Count self.logger.info(f"找到 {comp_count} 个元件") # 遍历所有元件 for i in range(comp_count): comp = self.sch_doc.Components.Item(i) # 提取元件信息 comp_info = { 'index': i + 1, 'designator': comp.Designator if hasattr(comp, 'Designator') else 'Unknown', 'comment': comp.Comment if hasattr(comp, 'Comment') else '', 'footprint': comp.Footprint if hasattr(comp, 'Footprint') else '', 'library': comp.LibraryName if hasattr(comp, 'LibraryName') else '', 'quantity': 1, 'x': UnitConverter.nm_to_mm(comp.LocationX) if hasattr(comp, 'LocationX') else 0, 'y': UnitConverter.nm_to_mm(comp.LocationY) if hasattr(comp, 'LocationY') else 0, } # 提取参数 if hasattr(comp, 'Parameters'): params = {} for j in range(comp.Parameters.Count): param = comp.Parameters.Item(j) params[param.Name] = param.Value comp_info['parameters'] = params components.append(comp_info) # 进度提示 if (i + 1) % 50 == 0: self.logger.info(f"已处理 {i + 1}/{comp_count} 个元件") self.logger.info(f"元件提取完成,共 {len(components)} 个") except Exception as e: self.logger.error(f"提取元件时出错: {str(e)}") return [] return components def generate_bom_csv(self, components, output_path): """生成CSV格式的BOM文件""" self.logger.info(f"生成BOM文件: {output_path}") # 定义CSV列 fieldnames = [ '序号', '标号', '型号', '封装', '库名称', '数量', 'X坐标(mm)', 'Y坐标(mm)', '备注' ] # 写入CSV文件 try: with open(output_path, 'w', newline='', encoding='utf-8-sig') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for comp in components: # 准备行数据 row = { '序号': comp['index'], '标号': comp['designator'], '型号': comp['comment'], '封装': comp['footprint'], '库名称': comp['library'], '数量': comp['quantity'], 'X坐标(mm)': f"{comp['x']:.2f}", 'Y坐标(mm)': f"{comp['y']:.2f}", '备注': '' } # 添加关键参数到备注 if 'parameters' in comp: params = comp['parameters'] if 'Value' in params: row['备注'] += f"值: {params['Value']}; " if 'Tolerance' in params: row['备注'] += f"容差: {params['Tolerance']}; " writer.writerow(row) self.logger.info(f"BOM文件生成成功: {output_path}") return True except Exception as e: self.logger.error(f"生成BOM文件失败: {str(e)}") return False def run(self, output_dir=None): """主执行函数""" # 初始化 if not self.initialize(): return False # 设置输出目录 if not output_dir: # 默认保存到桌面 desktop = os.path.join(os.path.expanduser("~"), "Desktop") output_dir = os.path.join(desktop, "AD_BOM_Exports") os.makedirs(output_dir, exist_ok=True) # 生成输出文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") sch_name = os.path.splitext(self.sch_doc.DocumentName) output_file = os.path.join(output_dir, f"BOM_{sch_name}_{timestamp}.csv") # 提取元件 components = self.extract_components() if not components: self.logger.warning("未找到元件,BOM为空") return False # 生成BOM文件 success = self.generate_bom_csv(components, output_file) # 完成提示 if success: self.logger.section("BOM导出完成") self.logger.info(f"文件位置: {output_file}") self.logger.info(f"元件数量: {len(components)}") # 在AD中显示完成消息 self.ad.ShowMessage("BOM导出完成", f"已导出 {len(components)} 个元件到:\n{output_file}") else: self.logger.error("BOM导出失败") return success # 脚本入口 if __name__ == "__main__": exporter = SchematicBOMExporter() exporter.run()这个脚本展示了AD二次开发的基本模式:
- 初始化连接:安全连接AD应用
- 获取文档对象:访问当前原理图
- 遍历对象集合:提取元件信息
- 数据处理:单位转换、信息整理
- 输出结果:生成CSV文件
- 用户反馈:显示完成消息