PCB 线路铜皮面积(残铜率)计算的实现方法
一个多月没更新博客园了,这里继续分享关于PCB工程相关一些知识,做过PCB工程都知道用使用genesis或incam是可以非常方便的计算得到铜皮面积这个参数【下图】,但实际这个软件是通过什么算法计算出铜面积的呢,这个我们不得而知,但接下来这里介绍一种可以将【线路铜皮面积(残铜率)】计算得出来的方法.
1.铜面积公式
公式=【铜面的多边形面积】+【铜的多边形周长*铜厚】-【孔的底面积】+【孔的圆柱面积】
注:看看计算公式是多么简单呀,是吧。下面重点讲【铜面的多边形面积】参数计算方法,因为其它参数过于简单就不写了
2.铜面积参数
1.表面铜面积【铜面的多边形面积】
2.表面横截面积【铜的多边形周长*铜厚】
3.有铜孔孔径面积【孔的底面积】
4.有铜孔孔壁面积【孔的圆柱面积】
【铜面的多边形面积】计算公式用 Shoelace公式 【鞋带公式】,此公式可以计算任意凸凹多边形,刚好是满足计算需求的,但对于PCB 铜皮(Surface)来说,铜皮点节点存在弧节点,直接用此公式计算当然不行啦,需要改造一下才行的。接一来用两种方法(丢失精度与精度)实现计算【铜面的多边形面积】
1.Shoelace公式 【鞋带公式】定义
公式图片来源引用 https://www.cnblogs.com/Khan-Sadas/p/10135717.html
定义:所述鞋带式或鞋带算法(也称为高斯的面积公式和测量员的式)是一种数学算法,以确定区域一个的简单多边形,其顶点由它们的描述笛卡尔坐标中的平面。用户交叉倍增相应的坐标,找到包含多边形的区域,并从周围的多边形中减去它,以找到其中的多边形区域。它被称为鞋带配方,因为构成多边形的坐标不断交叉倍增,就像绑鞋带一样。它有时也被称为鞋带方法
2.【鞋带公式】计算面积举例说明:
3. 方法一.【丢失精度】计算铜面积
将铜皮节点含有弧节点,全部转为折线节点, 转换后的弧长长度控制在0.1mm左右,当然弧长的长度值越小精度就越高,这样一来程序计算量就上去了,经测试弧长控制0.1mm比较合适。铜面积计算 精度不会丢失太多。
4.方法二.【精度】计算铜面积
将原有铜皮铜边形分为2部份
1.分解第1部份 折线多边形鞋带公式求解
2.分解第2部份 圆弧多边形扇形面积求解 (如何判断,圆弧多边形是删除,还是增加呢,下面有说明)
上面带来一个新的问题? 图形面积合并计算,如何判断,哪些弧形多边形是【加】还是【减】呢
按下表的关系进行加减计算合并铜皮面积
1.计算铜面积调用代码
//获取gtl 线路层(计算前转为Surface铜皮属性)gLayer workLayerInfo = g.getFEATURES("gtl");//1.【丢失精度】计算铜面积var areaLayer = calc2.s_area(workLayerInfo.Slist);//2. 【精度】计算铜面积var areaLayer2 = calc2.s_area2(workLayerInfo.Slist);//计算铜的多边形周长 var copperLenght = calc2.s_Length(workLayerInfo.Slist);
2.计算铜面积函数
/// <summary>/// 【丢失精度】计算铜面积/// </summary>/// <param name="gS_list"></param>/// <returns></returns>public double s_area(List<gS> gS_list){double SurfaceArea = 0;foreach (var gS_item in gS_list){foreach (var Polyline in gS_item.sur_group){var sur_list = s_2gSur_Point(Polyline.sur_list);if (Polyline.is_hole)SurfaceArea -= s_area(sur_list);elseSurfaceArea += s_area(sur_list);}}return SurfaceArea;}/// <summary>/// 【丢失精度】计算铜面积/// </summary>/// <param name="gSur_Point_list"></param>/// <returns></returns>public double s_area(List<gSur_Point> gSur_Point_list){int Point_Count = gSur_Point_list.Count() - 1;if (Point_Count < 2) return 0;double PolylineArea = 0;double ArcArea = 0;for (int i = 1; i <= Point_Count; i++){PolylineArea += gSur_Point_list[i - 1].p.x * gSur_Point_list[i].p.y - gSur_Point_list[i - 1].p.y * gSur_Point_list[i].p.x;}PolylineArea = Math.Abs(PolylineArea * 0.5);return PolylineArea;}/// <summary>/// 【精度】计算铜面积/// </summary>/// <param name="gSur_Point_list"></param>/// <returns></returns>public double s_area2(List<gSur_Point> gSur_Point_list){int Point_Count = gSur_Point_list.Count() - 1;if (Point_Count < 2) return 0;double PolylineArea = 0;double ArcArea = 0;bool isCCW = s_isCCW(gSur_Point_list);for (int i = 1; i <= Point_Count; i++){if (gSur_Point_list[i].type_point > 0){double a_area = a_Area(gSur_Point_list[i - 1].p, gSur_Point_list[i].p, gSur_Point_list[i + 1].p, gSur_Point_list[i].type_point == 2);if (isCCW){if (gSur_Point_list[i].type_point == 2)ArcArea += a_area;elseArcArea -= a_area;}else{if (gSur_Point_list[i].type_point == 2)ArcArea -= a_area;elseArcArea += a_area;}}PolylineArea += gSur_Point_list[i - 1].p.x * gSur_Point_list[i].p.y - gSur_Point_list[i - 1].p.y * gSur_Point_list[i].p.x;}PolylineArea = Math.Abs(PolylineArea * 0.5);PolylineArea += ArcArea;//var isCW = s_isCW(gSur_Point_list);//PolylineArea += (isCCW ? -ArcArea : ArcArea);return PolylineArea;}/// <summary>/// 求弧Arc 扇形面积/// </summary>/// <param name="a"></param>/// <returns></returns>public double a_Area(gPoint ps, gPoint pc, gPoint pe, bool ccw, bool islg180deg = false){double r_ = p2p_di(pc, ps);return pi * r_ * r_ * a_Angle(ps, pc, pe, ccw, islg180deg) / 360;}/// <summary>/// 求弧Arc圆心角 3点 //后续改进 用叉积 与3P求角度求解 验证哪个效率高/// </summary>/// <param name="ps"></param>/// <param name="pc"></param>/// <param name="pe"></param>/// <param name="ccw"></param>/// <returns></returns>public double a_Angle(gPoint ps, gPoint pc, gPoint pe, bool ccw, bool islg180deg = false){double angle_s, angle_e, angle_sum;if (ccw){angle_s = p_ang(pc, pe);angle_e = p_ang(pc, ps);}else{angle_s = p_ang(pc, ps);angle_e = p_ang(pc, pe);}if (angle_s == 360) { angle_s = 0; }if (angle_e >= angle_s){angle_sum = 360 - (angle_e - angle_s); //360 - Math.Abs(angle_s - angle_e); }else{angle_sum = angle_s - angle_e;//Math.Abs(angle_s - angle_e); }if (islg180deg && angle_sum > 180){angle_sum = 360 - angle_sum;}return angle_sum;}/// <summary>/// 检测 Surface是否逆时针 /// </summary>/// <param name="gSur_Point_list"></param>/// <returns></returns>public bool s_isCCW(List<gSur_Point> gSur_Point_list){double d = 0;int n = gSur_Point_list.Count() - 1;for (int i = 0; i < n; i++){if (gSur_Point_list[i].type_point > 0) continue;int NextI = i + 1 + (gSur_Point_list[i+ 1].type_point > 0 ? 1 : 0);d += -0.5 * (gSur_Point_list[NextI].p.y + gSur_Point_list[i].p.y) * (gSur_Point_list[NextI].p.x - gSur_Point_list[i].p.x);}return d > 0;}/// <summary>/// 将gSur_Point中含弧的节点转为线/// </summary>/// <param name="gSur_Point_list"></param>/// <param name="val_">此数值表示:分段数值</param>/// <param name="type_">代表值数值类型 【0】弧长 【1】角度 【2】弦长 </param>/// <returns></returns>public List<gSur_Point> s_2gSur_Point(List<gSur_Point> gSur_Point_list, double val_ = 1d, int type_ = 1){List<gSur_Point> resultList = new List<gSur_Point>();if (gSur_Point_list.Count > 2){bool is_flag = false;resultList.Add(gSur_Point_list[0]);for (int j = 1; j < gSur_Point_list.Count; j++){if (is_flag){is_flag = false;continue;}if (gSur_Point_list[j].type_point > 0){var aData = new gA(gSur_Point_list[j - 1].p, gSur_Point_list[j].p, gSur_Point_list[j + 1].p, 100, gSur_Point_list[j].type_point == 2 ? true : false);var PlistData = a_2Plist(aData, val_, type_, true);resultList.AddRange(PlistData.Select(tt => new gSur_Point(tt.p, 0)).ToList());is_flag = true;}else{resultList.Add(gSur_Point_list[j]);}}}return resultList;}/// <summary>/// 弧Arc 转点P组集/// </summary>/// <param name="a"></param>/// <param name="val_">此数值表示:分段数值</param>/// <param name="type_">代表值数值类型 【0】弧长 【1】角度 【2】弦长 </param>/// <param name="is_avg">是否平均分布 </param>/// <returns></returns>public List<gPP> a_2Plist(gA a, double val_ = 0.1d, int type_ = 0, bool is_avg = false){List<gPP> list_point = new List<gPP>();gPP tempP;tempP.p = a.ps;tempP.symbols = a.symbols;tempP.width = a.width;list_point.Add(tempP);double avg_count;double angle_val = 0;double rad_ = p2p_di(a.pc, a.pe);double sum_alge = a_Angle(a);if (type_ == 1) // 【1】角度 {angle_val = val_;avg_count = (int)(Math.Floor(sum_alge / angle_val)); // 总角度/单角度 }else if (type_ == 2) //【2】弦长 {angle_val = Math.Asin(val_ / (rad_ * 2)) * 360 / pi;avg_count = (int)(Math.Ceiling(sum_alge / angle_val) + eps) - 1; // 总角度/单弦长 }else // 【0】弧长 {angle_val = val_ * 180 / (pi * rad_);avg_count = (int)(Math.Ceiling(sum_alge / angle_val) + eps) - 1; // 总角度/单角度//avg_count = (int)(Math.Ceiling(a_Lenght(a) / val_)) - 1; // 或 总弧长/单弧长 }if (is_avg)angle_val = sum_alge / avg_count;if (avg_count > 1){gPP centerP = tempP;centerP.p = a.pc;double angle_s = p_ang(a.pc, a.ps);if (a.ccw) { angle_val = 0 - angle_val; }for (int i = 1; i < avg_count; i++){tempP = p_val_ang(centerP, rad_, angle_s - angle_val * i);list_point.Add(tempP);}}// if (!(zero(a.ps.x - a.pe.x) && zero(a.ps.y - a.pe.y)))// {// tempP.p = a.pe;// list_point.Add(tempP);// }tempP.p = a.pe;list_point.Add(tempP);return list_point;}/// <summary>/// 返回两点之间欧氏距离/// </summary>/// <param name="p1"></param>/// <param name="p2"></param>/// <returns></returns>public double p2p_di(gPoint p1, gPoint p2){return Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));}/// <summary>/// 求弧Arc圆心角 //后续改进 用叉积 与3P求角度求解 验证哪个效率高/// </summary>/// <param name="a"></param>/// <returns></returns>public double a_Angle(gA a){double angle_s, angle_e, angle_sum;if (a.ccw){angle_s = p_ang(a.pc, a.pe);angle_e = p_ang(a.pc, a.ps);}else{angle_s = p_ang(a.pc, a.ps);angle_e = p_ang(a.pc, a.pe);}if (angle_s == 360) { angle_s = 0; }if (angle_e >= angle_s)angle_sum = 360 - Math.Abs(angle_s - angle_e);elseangle_sum = Math.Abs(angle_s - angle_e);return angle_sum;}/// <summary>/// 求方位角/// </summary>/// <param name="ps"></param>/// <param name="pe"></param>/// <returns></returns>public double p_ang(gPoint ps, gPoint pe){double a_ang = Math.Atan((pe.y - ps.y) / (pe.x - ps.x)) / Math.PI * 180;//象限角 转方位角 计算所属象限 并求得方位角if (pe.x >= ps.x && pe.y >= ps.y) //↗ 第一象限 {return a_ang;}else if (!(pe.x >= ps.x) && pe.y >= ps.y) // ↖ 第二象限 {return a_ang + 180;}else if (!(pe.x >= ps.x) && !(pe.y >= ps.y)) //↙ 第三象限 {return a_ang + 180;}else if (pe.x >= ps.x && !(pe.y >= ps.y)) // ↘ 第四象限 {return a_ang + 360;}else{return a_ang;}}/// <summary>/// 求增量坐标/// </summary>/// <param name="ps">起点</param>/// <param name="val">增量值</param>/// <param name="ang_direction">角度</param>/// <returns></returns>public gPP p_val_ang(gPP ps, double val, double ang_direction){gPP pe = ps;pe.p.x = ps.p.x + val * Math.Cos(ang_direction * Math.PI / 180);pe.p.y = ps.p.y + val * Math.Sin(ang_direction * Math.PI / 180);return pe;}
View Code
3.计算铜的多边形周长函数
/// <summary>/// 求Surface 总周长/// </summary>/// <param name="gS_list"></param>/// <returns></returns>public double s_Length(List<gS> gS_list){int Surface_Count = gS_list.Count();double SurfaceArea = 0;foreach (var gS_item in gS_list){foreach (var Polyline in gS_item.sur_group){SurfaceArea += s_Length(Polyline.sur_list);}}return SurfaceArea;}/// <summary>/// 求Surface 总周长/// </summary>/// <param name="gSur_Point_list"></param>/// <returns></returns>public double s_Length(List<gSur_Point> gSur_Point_list){double sum_lenght = 0;bool is_flag = false;bool ccw = false;for (int i = 1; i < gSur_Point_list.Count; i++){if (is_flag){is_flag = false;continue;}if (gSur_Point_list[i].type_point > 0){if (gSur_Point_list[i].type_point == 2)ccw = true;elseccw = false;sum_lenght += a_Length(gSur_Point_list[i - 1].p, gSur_Point_list[i].p, gSur_Point_list[i + 1].p, ccw);is_flag = true;}else{sum_lenght += l_Length(gSur_Point_list[i - 1].p, gSur_Point_list[i].p);}}return sum_lenght;}/// <summary>/// 求弧Arc长度 3点/// </summary>/// <param name="ps"></param>/// <param name="pc"></param>/// <param name="pe"></param>/// <returns></returns>public double a_Length(gPoint ps, gPoint pc, gPoint pe, bool ccw = false){return pi / 180 * p2p_di(pc, ps) * a_Angle(ps, pc, pe, ccw);}/// <summary>/// 求线Line长度 2点/// </summary>/// <param name="ps"></param>/// <param name="pe"></param>/// <returns></returns>public double l_Length(gPoint ps, gPoint pe){return Math.Sqrt((ps.x - pe.x) * (ps.x - pe.x) + (ps.y - pe.y) * (ps.y - pe.y));}
View Code
4.数据结构
/// <summary>/// Surface 坐标泛型集类1/// </summary>public class gSur_Point{public gSur_Point(){ }public gSur_Point(double x_val, double y_val, byte type_point_){this.p.x = x_val;this.p.y = y_val;this.type_point = type_point_;}public gSur_Point(gPoint p, byte type_point_){this.p = p;this.type_point = type_point_;}public gPoint p;/// <summary>/// 0为折点 1为顺时针 2为逆时针 /// </summary>public byte type_point { get; set; } = 0;/// <summary>/// 值/// </summary>public double Value { get; set; } = 0;}/// <summary>/// Surface 坐标泛型集类2/// </summary>public class gSur_list{public List<gSur_Point> sur_list = new List<gSur_Point>();/// <summary>/// 是否为空洞/// </summary>public bool is_hole { get; set; }/// <summary>/// 是否逆时针/// </summary>public bool is_ccw { get; set; }}/// <summary>/// Surface 坐标泛型集类3/// </summary>public class gS{public List<gSur_list> sur_group = new List<gSur_list>();/// <summary>/// 是否为负 polarity-- P N/// </summary>public bool negative { get; set; }public string attribut { get; set; }}/// <summary>/// 点 数据类型 (XY)/// </summary>public struct gPoint{public gPoint(gPoint p_){this.x = p_.x;this.y = p_.y;}public gPoint(double x_val, double y_val){this.x = x_val;this.y = y_val;}public double x;public double y;public static gPoint operator +(gPoint p1, gPoint p2){p1.x += p2.x;p1.y += p2.y;return p1;}public static gPoint operator -(gPoint p1, gPoint p2){p1.x -= p2.x;p1.y -= p2.y;return p1;}}/// <summary>/// ARC 数据类型/// </summary>public struct gA{public gA(double ps_x, double ps_y, double pc_x, double pc_y, double pe_x, double pe_y, double width_, bool ccw_){this.ps = new gPoint(ps_x, ps_y);this.pc = new gPoint(pc_x, pc_y);this.pe = new gPoint(pe_x, pe_y);this.negative = false;this.ccw = ccw_;this.symbols = "r" + width_.ToString();this.attribut = string.Empty;this.width = width_;}public gA(gPoint ps_, gPoint pc_, gPoint pe_, double width_, bool ccw_ = false){this.ps = ps_;this.pc = pc_;this.pe = pe_;this.negative = false;this.ccw = ccw_;this.symbols = "r" + width_.ToString();this.attribut = string.Empty;this.width = width_;}public gPoint ps;public gPoint pe;public gPoint pc;public bool negative;//polarity-- positive negativepublic bool ccw; //direction-- cw ccwpublic string symbols;public string attribut;public double width;public static gA operator +(gA arc1, gPoint move_p){arc1.ps += move_p;arc1.pe += move_p;arc1.pc += move_p;return arc1;}public static gA operator +(gA arc1, gPP move_p){arc1.ps += move_p.p;arc1.pe += move_p.p;arc1.pc += move_p.p;return arc1;}public static gA operator +(gA arc1, gP move_p){arc1.ps += move_p.p;arc1.pe += move_p.p;arc1.pc += move_p.p;return arc1;}public static gA operator -(gA arc1, gPoint move_p){arc1.ps -= move_p;arc1.pe -= move_p;arc1.pc -= move_p;return arc1;}public static gA operator -(gA arc1, gPP move_p){arc1.ps -= move_p.p;arc1.pe -= move_p.p;arc1.pc -= move_p.p;return arc1;}public static gA operator -(gA arc1, gP move_p){arc1.ps -= move_p.p;arc1.pe -= move_p.p;arc1.pc -= move_p.p;return arc1;}}
View Code
【残铜率】公式=【铜皮面积】除以 【Profile面积】
正常来说profile形状为矩形(开料决定的),计算矩形面积非常容易,但如果是profile尺寸为异形,求profile的面积和铜面积计算方法也是一样的, 异形的profile数据结构和铜【Surface】数据结构类似的。下图以异形profile为例,计算残铜率结果和genesis保持一致
经测试,发现程序计算出来铜面积与genesis铜面积计算存在少量的偏差(猜测奥宝为了达到越大规模铜面积计算或计算铜面积前在做数据转换与检测,而采用丢失精度计算铜面积达到快速计算铜面积的目的),用此方法计算铜面积,不管是计算速度上,还是铜面积精度上面都已超越genesis计算(仅仅个人测试对比结果).
下例:genesis计算铜面积存一定偏差,实际PAD尺寸为4X3mm 面积为:12平方毫米 而genesis计算得到面积为12.004平方毫米
转载于:https://www.cnblogs.com/pcbren/p/10909585.html
PCB 线路铜皮面积(残铜率)计算的实现方法相关推荐
- 看了毁你三观的PCB设计理论 高速PCB外层还要不要覆铜了
转载于:http://www.eefocus.com/xfire/blog/15-08/318634_490cd.html 我们经常在教科书上或者IC原厂的PCB设计指南里看到,在layout的最后, ...
- 电路板测试机检测软件,PCB线路板板智能检测的方法
原标题:PCB线路板板智能检测的方法 根据PCB板的具体特点,选择在线测试的方法将一种或多种工序结合在一起,取长补短,综合运用.下面小编来介绍一下关于PCB线路板板智能检测的方法. 首先,在PCB板机 ...
- 设计一个长方形类。成员变量包括:长度和宽度,成员函数除包括计算周长和计算面积外, 还包括用set方法来设置长方形的长度和宽度,以及用get的方法来获得长方形的长度和宽度 最后,编写一个测试程序来测试所
本文为博主原创文章,未经博主允许不得转载. 版权为陈博超所有,第一次于2020年11月22日发表于BLOG上 本BLOG上原创文章未经本人许可,不得用于商业用途.转载请经允许后注明出处,否则保留追究法 ...
- PMP-商业论证中的财务测量指标-动态投资回收期、净现值、内部收益率、效益成本率计算
文章目录 前言 PMP-商业论证中的财务测量指标-动态投资回收期.净现值.内部收益率.效益成本率计算 1. 术语定义 2. 净现值(NPV) 2.1 利息与折现 2.2. 利息的计算方式 3. IRR ...
- java统计excel数据_数据分析实战——EXCEL实现复购率计算
这是数据分析实战的第三篇(复购率计算篇),本文提供真实数据(脱敏),并梳理复购率计算思路和技巧,通过15分钟的阅读和实践,读者即可根据自己的订单数据灵活计算业务涉及到的复购率了.(实战数据在文末) 相 ...
- 用python写一个迁徙率计算代码
我给你一个简单的python代码来计算迁徙率:# 迁徙率计算 总人口人数 population = 1000 年迁徙总人口 migrated_population = 200# 计算迁徙率 migra ...
- 【Python】基金/股票 最大回撤率计算与绘图详解(附源码和数据)
如果你想找的是求最大回撤的算法,请跳转:[Python] 使用动态规划求解最大回撤详解 [Python]基金/股票 最大回撤率计算与绘图详解(附源码和数据) 0. 起因 1. 大成沪深300指数A 5 ...
- 利用Gauss-Legendre 5点通用公式计算线路中边桩坐标并计算放样数据
.正算主程序 GSZS 10→DimZ "X0"?I:"Y0"?S:"K0" ?O:"F0"?G:"KN&q ...
- java三角形有关面积及周长的计算
Java三角形有关面积及周长的计算 import java.util.Scanner; //创建三角形的类 成员方法(判断能否构成三角形,以及计算三角形的面积和周长),如果输入错误 可重新输入 cla ...
最新文章
- 学习笔记之06-点语法
- oracle学习笔记(一)------oracle基础知识和基本sql语句
- HipChat上传文件报未知错误解决方案
- 【转】Windows8不联网直接安装.Net 3.5 Framework的方法
- C++ 使用move来删除用户指定的文件
- 【十】推荐系统遇到知识图谱RippleNet
- python in memory_使用 in_memory 工作空间的注意事项
- linux mplayer_移植mplayer播放器到EK200开发板的步骤浅析
- 程序员提高编程技术最有效的一件事?了解一下,迅速提升自己!
- C#:foreach语句,yield语句
- 一款HP的本本大家帮忙看一下
- 苹果激活锁怎么解除?手把手教你关闭激活锁
- 基于ORL数据集的使用最小分类器的人脸识别
- Linux CPU负载率的计算方式
- 解决firefox Windows与linux数据不同步的问题
- 使用 Wrapper 自定义SQL
- NGO招志愿者翻译,请分享
- 软著 --- 注册与实名认证
- 家庭网络搭建_家庭网络
- 从Vivado启动ModelSim时遇到的问题([USF-modelsim-8] Failed to find the pre-compiled simulation library!)