0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于PCB 板的边倒圆角实现方案解析

PCB线路板打样 来源:博客园 作者:pcbren 2021-03-02 14:11 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

PCB 外形是直角时, 通常工程制作外形 (锣带) 时, 会将直角或尖角的地方倒成圆角, 主要是为了防止板边容易划伤板且容易扎伤人

所以当客户没有特殊要求时, PCB 外形是直角一般会默认倒角 0.5mm 圆角(如下图所示)

一。 PCB 板边倒圆角点分析

原 PCB 外形 如下图图示: 看了这个 PCB 外形, 产生有 2 个问题点。

1. 外形中哪些点需倒圆角?

2. 如何怎么倒圆角?

1. 外形中哪些点需倒圆角?

看下图: PCB 外形倒圆角的点, 刚好就是我们凸包需求出的点, 接下来我们将玩转凸包了, 只要求出凸包, 那么就可以实现 PCB 板边倒圆角啦。

求凸包的算法: 我们可以借鉴算法导论中的查找凸包的算法(加以改进得到新的求凸包方法, 详见[方法一] 与[方法二] )

2. 如何怎么倒圆角?

在下面有说明倒角方法。

二。 求凸点

方法一求凸点:[采用多轮遍历, 一遍一遍将凹点踢除, 剩于的即是凸点]

方法一求凸点: 代码

/// 《summary》

/// 求最大多边形最大凸包 1 [采用多轮遍历将凹点踢除, 剩于的即是凸点]

/// 《/summary》

/// 《param name=“gSur_Point_list”》《/param》

/// 《returns》《/returns》

public List《gSur_Point》 s_convex_polyon1(List《gSur_Point》 gSur_Point_list)

{

add addCOM = new add();

bool isOK = true;

List《gSur_Point》 PointList = new List《gSur_Point》();

var isCCW = s_isCCW(gSur_Point_list);

int sum = gSur_Point_list.Count() - 1;

int n = gSur_Point_list.Count();

for (int i = 0; i 《n; i++)

{

int IndexPre = (i - 1) % sum;

if (IndexPre == -1) IndexPre = sum - 1;

int IndexCurrent = i % sum;

int IndexNext = (i + 1) % sum;

if (gSur_Point_list[IndexPre].type_point》 0) continue;

if (gSur_Point_list[IndexCurrent].type_point》 0) continue;

var multiVal = multi(gSur_Point_list[IndexPre].p, gSur_Point_list[IndexCurrent].p, gSur_Point_list[IndexNext].p);

if ((isCCW && multiVal》 0) || (!isCCW && multiVal 《0))

PointList.Add(gSur_Point_list[IndexCurrent]);

else

isOK = false;

}

List《gSur_Point》 Point2List = new List《gSur_Point》(PointList);

while (!isOK)

{

isOK = true;

PointList.Clear();

PointList.AddRange(Point2List);

Point2List.Clear();

sum = PointList.Count() - 1;

n = PointList.Count();

for (int i = 0; i 《n; i++)

{

int IndexPre = (i - 1) % sum;

if (IndexPre == -1) IndexPre = sum - 1;

int IndexCurrent = i % sum;

int IndexNext = (i + 1) % sum;

var multiVal = multi(PointList[IndexPre].p, PointList[IndexCurrent].p, PointList[IndexNext].p);

if ((isCCW && multiVal》 0) || (!isCCW && multiVal 《0))

Point2List.Add(PointList[IndexCurrent]);

else

isOK = false;

}

}

return Point2List;

}

方法二求凸包:[采用一边遍历找出凸点并加入队列, 并同时将队列中的凸点队列中找出凹点踢除]

方法二求凸包代码:

/// 《summary》

/// 求最大多边形最大凸包 2 [采用一边遍历找出凸点并加入队列, 并同时将队列中的凸点队列中找出凹点踢除]

/// 《/summary》

/// 《param name=“gSur_Point_list”》《/param》

/// 《returns》《/returns》

public List《gSur_Point》 s_convex_polyon2(List《gSur_Point》 gSur_Point_list)

{

Stack《gSur_Point》 StackPoint = new Stack《gSur_Point》();

var isCCW = s_isCCW(gSur_Point_list);

int sum = gSur_Point_list.Count() - 1;

int n = gSur_Point_list.Count();

for (int i = 0; i 《n; i++)

{

int IndexPre = (i - 1) % sum;

if (IndexPre == -1) IndexPre = sum - 1;

int IndexCurrent = i % sum;

int IndexNext = (i + 1) % sum;

if (gSur_Point_list[IndexPre].type_point》 0) continue;

if (gSur_Point_list[IndexCurrent].type_point》 0) continue;

var multiVal = multi(gSur_Point_list[IndexPre].p, gSur_Point_list[IndexCurrent].p, gSur_Point_list[IndexNext].p);

if ((isCCW && multiVal》 0) || (!isCCW && multiVal 《0))

{

L1:

if (StackPoint.Count》 1)

{

var Top1Point = StackPoint.Pop();

var Top2Point = StackPoint.Peek();

multiVal = multi(Top2Point.p, Top1Point.p, gSur_Point_list[IndexCurrent].p);

if ((isCCW && multiVal》 0) || (!isCCW && multiVal 《0))

StackPoint.Push(Top1Point);

else

goto L1;

}

StackPoint.Push(gSur_Point_list[IndexCurrent]);

}

}

return StackPoint.Reverse().ToList();

}

方法三求凸包:[按算法导论 Graham 扫描法 各节点按方位角 + 距离 逆时针排序 依次检查, 当不属凸点于则弹出]

方法三求凸包代码

/// 《summary》

/// 求最大多边形最大凸包 5 [按算法导论 Graham 扫描法 各节点按方位角 + 距离 逆时针排序 依次检查, 当不属凸点于则弹出]

/// 由于把各点的排列顺序重新排序了, 只支持折线节点(当存在弧节点时会出异常 !!!)

/// 《/summary》

/// 《param name=“gSur_Point_list”》《/param》

/// 《returns》《/returns》

public List《gSur_Point》 s_convex_polyon3(List《gSur_Point》 gSur_Point_list)

{

var LeftBottomPoint = gSur_Point_list.OrderBy(tt =》 tt.p.y).ThenBy(tt =》 tt.p.x).FirstOrDefault();

gSur_Point_list.RemoveAt(gSur_Point_list.Count - 1);

gSur_Point_list.ForEach(tt =》

{

tt.Value = p2p_di(LeftBottomPoint.p, tt.p);

tt.Angle = p_ang(LeftBottomPoint.p, tt.p);

}

);

gSur_Point_list = gSur_Point_list.OrderBy(tt =》 tt.Angle).ThenBy(tt =》 tt.Value).ToList();

gSur_Point_list.Add(gSur_Point_list[0]);

Stack《gSur_Point》 StackPoint = new Stack《gSur_Point》();

var isCCW = true;

int sum = gSur_Point_list.Count() - 1;

int n = gSur_Point_list.Count();

for (int i = 0; i 《n; i++)

{

int IndexPre = (i - 1) % sum;

if (IndexPre == -1) IndexPre = sum - 1;

int IndexCurrent = i % sum;

int IndexNext = (i + 1) % sum;

var multiVal = multi(gSur_Point_list[IndexPre].p, gSur_Point_list[IndexCurrent].p, gSur_Point_list[IndexNext].p);

if (isCCW && multiVal》 0)

{

L1:

if (StackPoint.Count》 1)

{

var Top1Point = StackPoint.Pop();

var Top2Point = StackPoint.Peek();

multiVal = multi(Top2Point.p, Top1Point.p, gSur_Point_list[IndexCurrent].p);

if (isCCW && multiVal》 0)

StackPoint.Push(Top1Point);

else

goto L1;

}

StackPoint.Push(gSur_Point_list[IndexCurrent]);

}

}

return StackPoint.Reverse().ToList();

}

公共方法与数据结构

/// 《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》

/// 角度

/// 《/summary》

public double Angle { get; set; } = 0;

/// 《summary》

/// 标记

/// 《/summary》

public bool isFalg { 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;

}

public static gPoint operator +(gPoint p1, double val)

{

p1.x += val;

p1.y += val;

return p1;

}

public static bool operator ==(gPoint p1, gPoint p2)

{

return (p1.x == p2.x && p1.y == p2.y);

}

public static bool operator !=(gPoint p1, gPoint p2)

{

return !(p1.x == p2.x && p1.y == p2.y);

}

}

/// 《summary》

/// 求叉积 判断[点 P 与线 L] 位置关系[小于 0] 在右边 [大于 0] 在左边 [等于 0] 共线

/// 《/summary》

/// 《param name=“ps”》《/param》

/// 《param name=“pe”》《/param》

/// 《param name=“p”》《/param》

/// 《returns》[小于 0] 在右边 [大于 0] 在左边 [等于 0] 共线《/returns》

public double multi(gPoint ps, gPoint pe, gPoint p)

{

return ((ps.x - p.x) * (pe.y - p.y) - (pe.x - p.x) * (ps.y - p.y));

}

/// 《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.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.p.y) * (gSur_Point_list[NextI].p.x - gSur_Point_list.p.x);

}

return d》 0;

}

/// 《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》

/// 求方位角

/// 《/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;

}

}

View Code

三。 板边凸点倒圆角方法

方法一。 也最简单的倒角方法, 我们将 PCB 板边凸点找出来后, 可以直接借助 genesis 倒角功能就可以实现了

当然但偶尔会报错的, 且当 N 个小线段组成的尖角倒角会出错(要实现完美效果只有自己写倒角算法啦)

方法二: 自己写倒角算法, 这个算法和加内角孔算法类似 (这里只是介绍简单的倒角) 考虑特殊的需要扩展

四。 凸点加倒圆角实现效果

编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • pcb
    pcb
    +关注

    关注

    4417

    文章

    23961

    浏览量

    426061
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    步进电机控制方案中驱动的核心作用与技术解析

    指令,通过能量转换、信号放大、闭环调节与安全保护,实现对步进电机的精准驱动。本文从步进电机控制的底层逻辑出发,系统拆解驱动在指令解析、功率驱动、运行优化、安全防护四大维度的核心作用,结合典型硬件架构与工程案例,揭示驱动
    发表于 04-10 15:34 232次阅读

    SiLM2648-AQ智能高开关:重新定义汽车电源的eFuse保护方案

    熔断(eFuse)解决方案,有效解决了传统方案过流保护不精准、故障诊断滞后等痛点。特性SiLM2648-AQ 围绕智能保护与精准控制设计,具备以下卓越特性: 高精度可编程电流检测:通过外部高分流
    发表于 03-20 08:20

    2025年PCB内置板载天线技术特点及应用方案解析

    随着无线通信技术的迅猛发展,PCB天线(Printed Circuit Board Antenna,印刷电路天线)凭借其小型化、成本低、易于批量生产和良好的性能,在物联网(IoT)、智能硬件、5G
    的头像 发表于 02-01 10:48 725次阅读

    pcb最少几块?

    BOM配单、SMT贴装及元器件采销等环节,详细解析行业规范与操作实践,并介绍一站式智造服务平台——拍明芯城(www.iczoom.com)的服务优势。 一、PCB的最小数量要求 PCB
    的头像 发表于 12-26 17:57 383次阅读
    <b class='flag-5'>pcb</b>打<b class='flag-5'>板</b>最少几块?

    主板到IO连接线核心技术与方案解析

    一、主板到IO连接线核心技术与双品电子FFCSP方案解析1.1核心定义与主要功能主板到IO连接线(又称
    的头像 发表于 12-24 17:53 477次阅读
    主板到IO<b class='flag-5'>板</b>连接线核心技术与<b class='flag-5'>方案</b><b class='flag-5'>解析</b>

    SCH1600 PCB 设计解析:助力快速原型开发

    SCH1600 PCB 设计解析:助力快速原型开发 在电子设计领域,快速原型开发是产品迭代和创新的关键环节。今天,我们就来深入了解一下 Murata 的 SCH1600 Chip Carrier
    的头像 发表于 12-16 16:35 432次阅读

    SCH1600 PCB规格与设计解析

    SCH1600 PCB规格与设计解析 在电子设计领域,快速原型开发是推动创新的关键环节。今天我们要探讨的SCH1600 Chip Carrier PCB,就是一款专为实现快速原型开发而
    的头像 发表于 12-16 15:50 612次阅读

    SycoTec自动换刀主轴:PCB加工的精度与效率革新方案

    ,推出专为PCB加工场景打造的自动换刀主轴系列,以“高转速、高精度、高稳定性”为核心,为PCB加工提供了革命性解决方案,成为电子制造企业的优选装备。
    的头像 发表于 12-16 09:32 394次阅读
    SycoTec自动换刀主轴:<b class='flag-5'>PCB</b>分<b class='flag-5'>板</b>加工的精度与效率革新<b class='flag-5'>方案</b>

    PCB打样避坑指南:PCB打样全流程解析

    及分析:   PCB打样注意事项 一、前期准备:确保原始数据精准解析 实物完整性检查 原始PCB需完整无损,残缺或损伤可能导致逆向工程
    的头像 发表于 11-04 09:23 1146次阅读
    <b class='flag-5'>PCB</b>抄<b class='flag-5'>板</b>打样避坑指南:<b class='flag-5'>PCB</b>抄<b class='flag-5'>板</b>打样全流程<b class='flag-5'>解析</b>

    PCB线路激光打码机:赋能电子制造的高效标识解决方案

    在电子制造行业飞速发展的浪潮中,PCB(PrintedCircuitBoard,印制电路)作为电子产品的“骨架”,其生产质量与追溯管理至关重要。而PCB线路激光打码机,作为
    的头像 发表于 09-22 10:35 1384次阅读
    <b class='flag-5'>PCB</b>线路<b class='flag-5'>板</b>激光打码机:赋能电子制造的高效标识解决<b class='flag-5'>方案</b>

    PCB全流程解析:从拆解到测试,技术要点全揭秘!

    一站式PCBA加工厂家今天为大家讲讲PCB的完整流程是什么?PCB的完整流程与技术要点。PCB
    的头像 发表于 07-26 16:22 2017次阅读

    PCBA加工必知!工艺预留的原因、方式与重要性全解析

    一站式PCBA加工厂家今天为大家讲讲PCBA加工中为什么要预留工艺?预留工艺的方式及重要性。在PCBA加工过程中,预留工艺是一个至关重要的环节。许多客户在设计电路时可能会忽略这
    的头像 发表于 06-24 09:15 931次阅读

    芯片内嵌式PCB封装技术方案解析&amp;quot;七部曲&amp;quot; | 第二曲:市场主流玩家与技术方案解读

    以下完整内容发表在「SysPro电力电子技术」知识星球-《芯片内嵌式PCB封装技术方案全面解析的七部曲》系列文章-SysPro原创文章,仅用于SysPro内部使用-本篇为节选,完整内容会在知识星球
    的头像 发表于 06-13 06:48 2975次阅读
    芯片内嵌式<b class='flag-5'>PCB</b>封装技术<b class='flag-5'>方案</b><b class='flag-5'>解析</b>&amp;quot;七部曲&amp;quot; | 第二曲:市场主流玩家与技术<b class='flag-5'>方案</b>解读

    一文读懂:单层、多层、特殊材质 PCB 加工方式全解析

    一站式PCBA加工厂家今天为大家讲讲单层、多层及特殊材质PCB的加工方式有哪些?单层、多层及特殊材质PCB加工方式。在电子产品制造过程中,PCB
    的头像 发表于 05-06 08:59 1141次阅读

    PCBA设计工艺:提升生产效率与精度的关键

    缘的尺寸、形状、过孔、切割方式等多方面的考虑。虽然在许多设计中,板材的内部结构可能会得到更多的关注,但工艺的设计同样是至关重要的,因为它直接影响到整个PCB的制造、组装、性能以及可靠性。 什么是PCBA设计工艺? PCBA设
    的头像 发表于 04-23 09:24 801次阅读