PCB genesis連孔加除毛刺孔(槽孔與槽孔)實現方法(三)
一.為什麽 連孔加除毛刺孔
原因是 PCB板材中含有玻璃纖維, 毛刺產生位置在於2個孔相交位置,由於此處鉆刀受力不均導致纖維切削不斷形成毛刺 ,為了解決這個問題:在鉆完2個連孔後,在相交處再鉆一個孔,並鉆進去一點(常規進去1-2mil),這樣就可以將纖維毛刺去除 (沒找到SLOT槽與SLOT槽的實物圖.就用SLOT槽與圓孔吧,產生毛刺效果也是一樣的)
PCB同行業毛刺問題處理辦法 鉆孔孔內毛刺問題分析改善報告
二.如何判斷除毛刺孔加多少個?
在PCB行業工程加除毛刺孔加多少個也沒有太明確的定義,只要滿足毛刺去除即可.
這裏我們把相交的SLOT槽分為2類,一類是十字形,另一類是T型,分別用實際的案例做以說明.
1.十字型交叉SLOT槽:
實例1:十字槽 加1個孔 需滿足2點需求
P1到P3 兩點距離 與 P2與P4 兩點距離相等
P1與P2 兩點距離 與 P2與P3兩點距離相差<0.5mm
實例2:十字槽 加2個孔或4個孔, 需滿足1點需求
P1到P3 兩點距離 與 P2與P4 兩點距離相等
實例3:十字槽 加3個孔或4個孔,不滿足以下條件時
P1到P3 兩點距離 與 P2與P4 兩點距離相等
P1與P2 兩點距離 與 P2與P3兩點距離相差<0.5mm
失效實例:十字槽鉆1個孔失效案例
2.T字型交叉SLOT槽:
實例1: T字槽 加1個孔, 需滿足以下條件時
(W1 * 0.5 + W1 * 0.707) < W2
實例2:T字槽 加2個孔, 不滿足以下條件時
(W1 * 0.5 + W1 * 0.707) < W2
三.連孔加除毛刺孔實現關鍵需求出參數
除毛刺孔,這裏列舉幾個關鍵參數,如下圖所示(因為求解的參數太多,畫圖不好呈現,具體請看下方的代碼)
1.T字型槽加1個孔
2.T字型槽加2個孔
3.十字型槽加孔和T字型加孔類型,具體看下方代碼
四.C#簡易代碼實現:
1.加除毛刺孔代碼
#region 加除毛刺孔 mcdrl gLayer glayer = g.getFEATURES($"{"drl"}", g.STEP, g.JOB, "mm", true); gL line1 = glayer.Llist[0]; gL line2 = glayer.Llist[1]; List<gP> gpList = calc2.l2l_IntersectHole(line1, line2, 0.05); addCOM.pad(gpList); #endregionView Code
2.計算函數
/// <summary> /// 線段與線段(2個SLOT槽相交) 加除毛刺孔 /// </summary> /// <param name="l1"></param> /// <param name="l2"></param> /// <param name="CutInner">切入值</param> /// <returns></returns> public List<gP> l2l_IntersectHole(gL l1, gL l2, double CutInner = 0.05) { add addCOM = new add(); List<gP> gpList = new List<gP>(); gL l1_L, l1_R, l2_L, l2_R; int jd1Type = 0, jd2Type = 0, jd3Type = 0, jd4Type = 0; gPoint jdP1, jdP2, jdP3, jdP4; double ang1, ang2, ang3, ang4; gPoint HoleP1, HoleP2, HoleP3, HoleP4; if (multi(l1.ps, l1.pe, l2.ps) < 0 && multi(l1.ps, l1.pe, l2.pe) > 0) { gL tempL = l1; l1 = l2; l2 = tempL; } double l1Rval = l1.width * 0.0005; double l2Rval = l2.width * 0.0005; double Rval = l1Rval > l2Rval ? l2Rval : l1Rval; double Hole_Radius = Rval; double HoleSize = Hole_Radius * 2; l_offset(l1, l1Rval, out l1_L, out l1_R); l_offset(l2, l2Rval, out l2_L, out l2_R); jdP1 = l2l_Intersect(l1_L, l2_R, ref jd1Type); jdP2 = l2l_Intersect(l1_L, l2_L, ref jd2Type); jdP3 = l2l_Intersect(l1_R, l2_L, ref jd3Type); jdP4 = l2l_Intersect(l1_R, l2_R, ref jd4Type); jd1Type = jd1Type < 0 ? 0 : jd1Type; jd2Type = jd2Type < 0 ? 0 : jd2Type; jd3Type = jd3Type < 0 ? 0 : jd3Type; jd4Type = jd4Type < 0 ? 0 : jd4Type; if ((jd1Type + jd2Type + jd3Type + jd4Type) == 4) //產生4個交點 { if (Math.Abs(p2p_di(jdP1, jdP3) - p2p_di(jdP2, jdP4)) < 0.01) //4個交點且交錯交點相等時 加1個孔 { if (Math.Abs(p2p_di(jdP1, jdP2) - p2p_di(jdP2, jdP3)) < 0.51) //4個交點且2條槽寬相差小於0.5mm 加1個孔 { gPoint PointCenter = p2p_centerP(jdP1, jdP3); Hole_Radius = p2p_di(PointCenter, jdP1) + CutInner; HoleSize = ((int)(Math.Ceiling((Hole_Radius * 2 * 1000) / 50)) * 50) * 0.001; ; gpList.Add(new gP(PointCenter, HoleSize * 1000)); return gpList; } } } if ((jd1Type + jd2Type + jd3Type + jd4Type) == 2) //產生2個交點 { //P2 P3 //P1 P4 gPoint LineCenter = new gPoint(); double AngDirdction = 0; double ValDirdction = 0; double ValR = 0; double Lwidth = 0; if (jd1Type + jd2Type == 2) { LineCenter = p2p_centerP(jdP1, jdP2); AngDirdction = p_ang(LineCenter, l1_R); ValDirdction = p2p_di(jdP1, jdP2) * 0.5; ValR = ValDirdction * 1.4142136; Lwidth = l1Rval * 2; } if (jd2Type + jd3Type == 2) { LineCenter = p2p_centerP(jdP2, jdP3); AngDirdction = p_ang(LineCenter, l2_R); ValDirdction = p2p_di(jdP2, jdP3) * 0.5; ValR = ValDirdction * 1.4142136; Lwidth = l2Rval * 2; } if (jd3Type + jd4Type == 2) { LineCenter = p2p_centerP(jdP3, jdP4); AngDirdction = p_ang(LineCenter, l1_L); ValDirdction = p2p_di(jdP3, jdP4) * 0.5; ValR = ValDirdction * 1.4142136; Lwidth = l1Rval * 2; } if (jd4Type + jd1Type == 2) { LineCenter = p2p_centerP(jdP4, jdP1); AngDirdction = p_ang(LineCenter, l2_L); ValDirdction = p2p_di(jdP4, jdP1) * 0.5; ValR = ValDirdction * 1.4142136; Lwidth = l2Rval * 2; } Hole_Radius = ValR + CutInner; HoleSize = ((int)(Math.Ceiling((Hole_Radius * 2 * 1000) / 50)) * 50) * 0.001; Hole_Radius = HoleSize * 0.5; double diffVal = Hole_Radius - (ValR + CutInner); if (diffVal > 0) { ValDirdction = Math.Sqrt(Math.Pow((ValR + diffVal), 2) - Math.Pow((ValDirdction), 2)); } if ((ValDirdction + Hole_Radius - 0.1) < Lwidth) //2個交點且正交時 加1個孔 { gPoint PointCenter = p_val_ang(LineCenter, ValDirdction, AngDirdction); gpList.Add(new gP(PointCenter, HoleSize * 1000)); return gpList; } } //當不滿足條件的,全按一個交點加一個孔的方式處理 HoleSize = ((int)(Math.Ceiling((Rval * 2 * 1000) / 50)) * 50) * 0.001; Hole_Radius = HoleSize * 0.5; ang1 = a_AngleCenter_dirdction(jdP2, jdP1, jdP4); ang2 = a_AngleCenter_dirdction(jdP3, jdP2, jdP1); ang3 = p_ang_invert(ang1); ang4 = p_ang_invert(ang2); HoleP1 = p_val_ang(jdP1, Hole_Radius - CutInner, ang1); HoleP2 = p_val_ang(jdP2, Hole_Radius - CutInner, ang2); HoleP3 = p_val_ang(jdP3, Hole_Radius - CutInner, ang3); HoleP4 = p_val_ang(jdP4, Hole_Radius - CutInner, ang4); if (jd1Type == 1) gpList.Add(new gP(HoleP1, HoleSize * 1000)); if (jd2Type == 1) gpList.Add(new gP(HoleP2, HoleSize * 1000)); if (jd3Type == 1) gpList.Add(new gP(HoleP3, HoleSize * 1000)); if (jd4Type == 1) gpList.Add(new gP(HoleP4, HoleSize * 1000)); return gpList; } /// <summary> /// 線Line偏移 out 左與右偏移線Line /// </summary> /// <param name="l"></param> /// <param name="offset_val">偏移數值</param> /// <param name="left_l">out 左偏移線L</param> /// <param name="rithg_l">out 右偏移線L</param> public void l_offset(gL l, double offset_val, out gL left_l, out gL rithg_l) { left_l = l; rithg_l = l; double angle_ = p_ang(l.ps, l.pe); left_l.ps = p_val_ang(l.ps, offset_val, angle_ + 90); left_l.pe = p_val_ang(l.pe, offset_val, angle_ + 90); rithg_l.ps = p_val_ang(l.ps, offset_val, angle_ - 90); rithg_l.pe = p_val_ang(l.pe, offset_val, angle_ - 90); } /// <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 gPoint p_val_ang(gPoint ps, double val, double ang_direction) { gPoint pe; pe.x = ps.x + val * Math.Cos(ang_direction * Math.PI / 180); pe.y = ps.y + val * Math.Sin(ang_direction * Math.PI / 180); return pe; } /// <summary> /// 求線段與線段相交點 (線段與線段相差微小距離誤差無法可控 之前測試有發現後來沒發現了,有待驗證) /// </summary> /// <param name="L1"></param> /// <param name="L2"></param> /// <param name="isIntersectType">0平行無交點 1本身相交 -1本身不相交(但延長相交)</param> /// <returns></returns> public gPoint l2l_Intersect(gL L1, gL L2, ref int isIntersectType) { return l2l_Intersect(L1.ps, L1.pe, L2.ps, L2.pe, ref isIntersectType); } /// <summary> /// 求線段與線段相交點 (線段與線段相差微小距離誤差無法可控 之前測試有發現後來沒發現了,有待驗證) /// </summary> /// <param name="l1ps"></param> /// <param name="l1pe"></param> /// <param name="l2ps"></param> /// <param name="l2pe"></param> /// <param name="isIntersectType">0平行無交點 1本身相交 -1本身不相交(但延長相交)</param> /// <returns></returns> public gPoint l2l_Intersect(gPoint l1ps, gPoint l1pe, gPoint l2ps, gPoint l2pe, ref int isIntersectType) { gL L1 = new gL(l1ps, l1pe, 0); gL L2 = new gL(l2ps, l2pe, 0); gPoint tempP = new gPoint(); double ABC, ABD, CDA, CDB, T; //面積符號相同則兩點在線段同側,不相交 (對點在線段上的情況,本例當作不相交處理) ABC = (L1.ps.x - L2.ps.x) * (L1.pe.y - L2.ps.y) - (L1.ps.y - L2.ps.y) * (L1.pe.x - L2.ps.x); ABD = (L1.ps.x - L2.pe.x) * (L1.pe.y - L2.pe.y) - (L1.ps.y - L2.pe.y) * (L1.pe.x - L2.pe.x); CDA = (L2.ps.x - L1.ps.x) * (L2.pe.y - L1.ps.y) - (L2.ps.y - L1.ps.y) * (L2.pe.x - L1.ps.x); // 三角形cda 面積的2倍 // 註意: 這裏有一個小優化.不需要再用公式計算面積,而是通過已知的三個面積加減得出. CDB = CDA + ABC - ABD; // 三角形cdb 面積的2倍 if (Math.Abs(ABC - ABD) <= 0.000001) { isIntersectType = 0; //平行 return tempP; } else { if ((CDA * CDB <= 0.000001) && (ABC * ABD <= 0.000001)) //本身相交 { isIntersectType = 1; } else { isIntersectType = -1; //延長相交 } } //計算交點 T = CDA / (ABD - ABC); tempP.x = L1.ps.x + T * (L1.pe.x - L1.ps.x); tempP.y = L1.ps.y + T * (L1.pe.y - L1.ps.y); return tempP; } /// <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> /// 求點到點 中心點P /// </summary> /// <param name="l"></param> /// <returns></returns> public gPoint p2p_centerP(gPoint p1, gPoint p2) { gPoint tempP; tempP.x = (p1.x + p2.x) * 0.5; tempP.y = (p1.y + p2.y) * 0.5; return tempP; } /// <summary> /// 求增量坐標 /// </summary> /// <param name="ps">起點</param> /// <param name="val">增量值</param> /// <param name="ang_direction">角度</param> /// <returns></returns> public gPoint p_val_ang(gPoint ps, double val, double ang_direction) { gPoint pe; pe.x = ps.x + val * Math.Cos(ang_direction * Math.PI / 180); pe.y = ps.y + val * Math.Sin(ang_direction * Math.PI / 180); return pe; } /// <summary> /// 求弧Arc 弧中心方位角 /// </summary> /// <param name="ps"></param> /// <param name="pc"></param> /// <param name="pe"></param> /// <param name="ccw"></param> /// <returns></returns> public double a_AngleCenter_dirdction(gPoint ps, gPoint pc, gPoint pe, bool ccw = false) { double angle_s, angle_e, angle_sum, center_dirdction; 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); center_dirdction = (angle_s + angle_e) * 0.5 + 180; } else { angle_sum = angle_s - angle_e; center_dirdction = (angle_s + angle_e) * 0.5; } if (center_dirdction > 360) { center_dirdction = center_dirdction - 360; } return center_dirdction; } /// <summary> /// 求反方位角 /// </summary> /// <param name="ang_direction"></param> /// <returns></returns> public double p_ang_invert(double ang_direction)//求反方位角 { if (ang_direction >= 180) return ang_direction - 180; else return ang_direction + 180; }View Code
3.Point,PAD;Line;Arc數據結構
/// <summary> /// Line 數據類型 /// </summary> public struct gL { public gL(double ps_x, double ps_y, double pe_x, double pe_y, double width_) { this.ps = new gPoint(ps_x, ps_y); this.pe = new gPoint(pe_x, pe_y); this.negative = false; this.symbols = "r"; this.attribut = string.Empty; this.width = width_; } public gL(gPoint ps_, gPoint pe_, double width_) { this.ps = ps_; this.pe = pe_; this.negative = false; this.symbols = "r"; this.attribut = string.Empty; this.width = width_; } public gL(gPoint ps_, gPoint pe_, string symbols_, double width_) { this.ps = ps_; this.pe = pe_; this.negative = false; this.symbols = symbols_; this.attribut = string.Empty; this.width = width_; } public gPoint ps; public gPoint pe; public bool negative;//polarity-- positive negative public string symbols; public string attribut; public double width; public static gL operator +(gL l1, gPoint move_p) { l1.ps += move_p; l1.pe += move_p; return l1; } public static gL operator +(gL l1, gP move_p) { l1.ps += move_p.p; l1.pe += move_p.p; return l1; } public static gL operator -(gL l1, gPoint move_p) { l1.ps -= move_p; l1.pe -= move_p; return l1; } public static gL operator -(gL l1, gP move_p) { l1.ps -= move_p.p; l1.pe -= move_p.p; return l1; } } /// <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"; 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"; this.attribut = string.Empty; this.width = width_; } public gPoint ps; public gPoint pe; public gPoint pc; public bool negative;//polarity-- positive negative public bool ccw; //direction-- cw ccw public 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, 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, gP move_p) { arc1.ps -= move_p.p; arc1.pe -= move_p.p; arc1.pc -= move_p.p; return arc1; } } /// <summary> /// PAD 數據類型 /// </summary> public struct gP { public gP(double x_val, double y_val, double width_) { this.p = new gPoint(x_val, y_val); this.negative = false; this.angle = 0; this.mirror = false; this.symbols = "r"; this.attribut = string.Empty; this.width = width_; } public gPoint p; public bool negative;//polarity-- positive negative public double angle; public bool mirror; public string symbols; public string attribut; public double width; public static gP operator +(gP p1, gP p2) { p1.p += p2.p; return p1; } public static gP operator -(gP p1, gP p2) { p1.p -= p2.p; return p1; } } /// <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; } }View Code
五.在Genesis或Incam中如何判斷是否為連孔
判斷2個孔是否為連孔,可以自己寫算法實現啦,當然更多人還是會選擇奧寶提供DrillChecklist分析出來的的結果來判斷是否為連孔.因為你自己寫的算法效率沒有奧寶的效率高呀
六.實現效果
PCB genesis連孔加除毛刺孔(槽孔與槽孔)實現方法(三)