pcb-stator-coil-generator/coil_generator-12.ipynb

494 lines
18 KiB
Text
Raw Normal View History

2022-10-10 18:45:58 +00:00
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"\n",
"import matplotlib.pyplot as plt\n",
"from skspatial.objects import LineSegment, Line, Vector\n",
"\n",
"# some helper functions\n",
"from helpers import (\n",
" get_arc_point,\n",
" draw_arc,\n",
" rotate,\n",
" translate,\n",
" flip_y,\n",
" flip_x,\n",
2022-10-12 09:33:06 +00:00
" optimize_points,\n",
" chaikin,\n",
")\n",
"from pcb_json import (\n",
" dump_json,\n",
" plot_json,\n",
" create_via,\n",
" create_pad,\n",
2022-10-12 09:33:06 +00:00
" create_pin,\n",
" create_track,\n",
" create_silk,\n",
" create_silk,\n",
2022-10-12 09:33:06 +00:00
" create_mounting_hole,\n",
")\n",
"\n",
2022-10-10 18:45:58 +00:00
"from enum import Enum\n",
"\n",
"Layer = Enum(\"Layer\", \"FRONT BACK\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"VIA_DIAM = 0.8\n",
"VIA_DRILL = 0.4\n",
"# this is for a 1.27mm pitch pin\n",
2022-10-12 09:33:06 +00:00
"PIN_DIAM = 1.0\n",
"PIN_DRILL = 0.65\n",
"\n",
"# this is for the PCB connector - see https://www.farnell.com/datasheets/2003059.pdf\n",
"PAD_WIDTH = 6\n",
"PAD_HEIGHT = 2\n",
"PAD_PITCH = 2.5\n",
"\n",
"# where to put the input pads\n",
"INPUT_PAD_RADIUS = 19.5\n",
"\n",
"# PCB Edge size\n",
2022-10-12 09:33:06 +00:00
"STATOR_RADIUS = 23\n",
"STATOR_HOLE_RADIUS = 5\n",
"SCREW_HOLE_RADIUS = 20\n",
"SCREW_HOLE_DRILL_DIAM = 3.2 # 3.2mm drill for a 3mm screw\n",
2022-10-10 18:45:58 +00:00
"STATOR_HOLE_RADIUS = 5\n",
"\n",
"# Track width and spacing\n",
2022-10-10 18:45:58 +00:00
"TRACK_WIDTH = 0.127\n",
"TRACK_SPACING = 0.127\n",
"\n",
"# Coil params\n",
2022-10-10 18:45:58 +00:00
"TURNS = 9\n",
"COIL_CENTER_RADIUS = 11.5\n",
"COIL_VIA_RADIUS = 12.5\n",
"\n",
"# where to place the pins\n",
"CONNECTION_PINS_RADIUS = 16.5\n",
2022-10-10 18:45:58 +00:00
"\n",
"USE_SPIRAL = False"
2022-10-10 18:45:58 +00:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Arbitrary Coil Generation"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# templates must be simetric around the X axis and must include the center points on both size (e.g. (X1, 0).... (X2, 0) )\n",
"# template must also be convex\n",
"template = [\n",
" (-1.5, 0),\n",
" (-1.5, -0.1),\n",
" (1.9, -0.8),\n",
" (1.9, 0.0),\n",
" (1.9, 0.8),\n",
" (-1.5, 0.1),\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# plot the template shape wrapping around to the first point\n",
"df = pd.DataFrame(template + [template[0]], columns=[\"x\", \"y\"])\n",
"ax = df.plot.line(x=\"x\", y=\"y\", color=\"blue\")\n",
"ax.axis(\"equal\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def calculate_point(point, point1, point2, spacing, turn):\n",
" reference_vector = Vector([-100, 0])\n",
" angle = np.rad2deg(Vector(point).angle_between(reference_vector))\n",
" if point[1] > 0:\n",
" angle = 360 - angle\n",
" vector = Vector(point1) - Vector(point2)\n",
" normal = vector / np.linalg.norm(vector)\n",
" # rotate the vector 90 degrees\n",
" normal = np.array([-normal[1], normal[0]])\n",
" # move the point along the normal vector by the spacing\n",
" offset = spacing * (turn * 360 + angle) / 360\n",
" coil_point = point + normal * offset\n",
" return (coil_point[0], coil_point[1])\n",
"\n",
"\n",
"def get_points(template, turns, spacing):\n",
" coil_points = []\n",
" reference_vector = Vector([-100, 0])\n",
" template_index = 0\n",
" template_length = len(template)\n",
" for turn in range(turns * template_length):\n",
" point1 = template[template_index % template_length]\n",
" point2 = template[(template_index + 1) % template_length]\n",
"\n",
" # calculate the new positions of the points\n",
" coil_point1 = calculate_point(\n",
" point1, point1, point2, spacing, template_index // template_length\n",
" )\n",
" coil_point2 = calculate_point(\n",
" point2, point1, point2, spacing, (template_index + 1) // template_length\n",
" )\n",
" # adjust the previous point so that the previous line intersects with this new line\n",
" # this prevents any cutting of corners\n",
" if len(coil_points) >= 2:\n",
" # create a line from the previous two points\n",
" line1 = Line(\n",
" coil_points[len(coil_points) - 2],\n",
" np.array(coil_points[len(coil_points) - 1])\n",
" - np.array(coil_points[len(coil_points) - 2]),\n",
" )\n",
" # create a line from the two new points\n",
" line2 = Line(\n",
" np.array(coil_point1),\n",
" np.array(np.array(coil_point1) - np.array(coil_point2)),\n",
" )\n",
" # find the intersection of the two lines\n",
" try:\n",
" intersection = line1.intersect_line(line2)\n",
" # replace the previous point with the intersection\n",
" coil_points[len(coil_points) - 1] = intersection\n",
" # add the new point\n",
" coil_points.append(coil_point2)\n",
" except:\n",
" # the lines did not intersect so just add the points\n",
" coil_points.append(coil_point1)\n",
" coil_points.append(coil_point2)\n",
" else:\n",
" coil_points.append(coil_point1)\n",
" coil_points.append(coil_point2)\n",
"\n",
" template_index = template_index + 1\n",
" return coil_points"
2022-10-10 18:45:58 +00:00
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"template_f = []\n",
"for i in range(len(template)):\n",
" template_f.append(template[len(template) - i - len(template) // 2])\n",
"template_f = flip_x(template_f)\n",
"points_f = chaikin(\n",
" optimize_points(flip_x(get_points(template_f, TURNS, TRACK_SPACING + TRACK_WIDTH))),\n",
" 2,\n",
")\n",
"points_b = chaikin(\n",
" optimize_points(get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH)), 2\n",
")\n",
2022-10-10 18:45:58 +00:00
"\n",
"points_f = [(COIL_VIA_RADIUS - COIL_CENTER_RADIUS, 0)] + points_f\n",
"points_b = [(COIL_VIA_RADIUS - COIL_CENTER_RADIUS, 0)] + points_b\n",
2022-10-10 18:45:58 +00:00
"\n",
"df = pd.DataFrame(points_f, columns=[\"x\", \"y\"])\n",
"ax = df.plot.line(x=\"x\", y=\"y\", color=\"blue\")\n",
"ax.axis(\"equal\")\n",
"df = pd.DataFrame(points_b, columns=[\"x\", \"y\"])\n",
"ax = df.plot.line(x=\"x\", y=\"y\", color=\"red\", ax=ax)\n",
2022-10-10 18:45:58 +00:00
"\n",
"print(\"Track points\", len(points_f), len(points_b))"
2022-10-10 18:45:58 +00:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generate PCB Layout"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# calculat the total length of the track to compute the resistance\n",
"total_length_front = 0\n",
"for i in range(len(points_f) - 1):\n",
" total_length_front += np.linalg.norm(\n",
" np.array(points_f[i + 1]) - np.array(points_f[i])\n",
" )\n",
"print(\"Total length front\", total_length_front)\n",
"\n",
"total_length_back = 0\n",
"for i in range(len(points_b) - 1):\n",
" total_length_back += np.linalg.norm(\n",
" np.array(points_b[i + 1]) - np.array(points_b[i])\n",
" )\n",
"print(\"Total length back\", total_length_back)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vias = []\n",
"tracks_f = []\n",
"tracks_b = []\n",
"pads = []\n",
2022-10-12 09:33:06 +00:00
"pins = []\n",
"mounting_holes = []\n",
2022-10-10 18:45:58 +00:00
"silk = []\n",
"\n",
"# create the pads at CONNECTION_PINS radius - 2 for each of the coils, A, B and C\n",
"# angle_A = 0\n",
"# pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_A - 30, \"A\"))\n",
"# pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_A + 30, \"A\"))\n",
"\n",
"# angle_B = 120\n",
"# pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_B - 30, \"B\"))\n",
"# pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_B + 30, \"B\"))\n",
"\n",
"# angle_C = 240\n",
"# pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_C - 30, \"C\"))\n",
"# pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_C + 30, \"C\"))\n",
"\n",
"\n",
"# the main coils\n",
"coil_labels = [\"A\", \"B\", \"C\"]\n",
"coils_f = []\n",
"coils_b = []\n",
"for i in range(12):\n",
" angle = i * 360 / 12\n",
" if (i // 3) % 2 == 0:\n",
" coil_A_f = translate(rotate(points_f, angle), COIL_CENTER_RADIUS, angle)\n",
" coil_A_b = translate(rotate(points_b, angle), COIL_CENTER_RADIUS, angle)\n",
" else:\n",
" coil_A_f = translate(rotate(flip_y(points_f), angle), COIL_CENTER_RADIUS, angle)\n",
" coil_A_b = translate(rotate(flip_y(points_b), angle), COIL_CENTER_RADIUS, angle)\n",
" # keep track of the coils\n",
" coils_f.append(coil_A_f)\n",
" coils_b.append(coil_A_b)\n",
"\n",
" tracks_f.append(coil_A_f)\n",
" tracks_b.append(coil_A_b)\n",
" vias.append(create_via(get_arc_point(angle, COIL_VIA_RADIUS)))\n",
" silk.append(\n",
" create_silk(get_arc_point(angle, COIL_CENTER_RADIUS), coil_labels[i % 3])\n",
" )\n",
"\n",
"# raidus for connecting the bottoms of the coils together\n",
"connection_radius1 = STATOR_HOLE_RADIUS + TRACK_SPACING + TRACK_WIDTH\n",
"\n",
"# create tracks to link the A coils around the center\n",
"connection_via_radius_A = connection_radius1 + TRACK_SPACING + VIA_DIAM / 2\n",
"coil_A1_A2_inner = (\n",
" [get_arc_point(0, connection_via_radius_A)]\n",
" + draw_arc(0, 3 * 360 / 12, connection_radius1)\n",
" + [get_arc_point(3 * 360 / 12, connection_via_radius_A)]\n",
")\n",
"tracks_f.append(coil_A1_A2_inner)\n",
"coil_A3_A4_inner = (\n",
" [get_arc_point(6 * 360 / 12, connection_via_radius_A)]\n",
" + draw_arc(6 * 360 / 12, 9 * 360 / 12, connection_radius1)\n",
" + [get_arc_point(9 * 360 / 12, connection_via_radius_A)]\n",
")\n",
"tracks_f.append(coil_A3_A4_inner)\n",
"# connect up the bottoms of the A coils\n",
"coils_b[0].append(coil_A1_A2_inner[0])\n",
"coils_b[3].append(coil_A1_A2_inner[-1])\n",
"coils_b[6].append(coil_A3_A4_inner[0])\n",
"coils_b[9].append(coil_A3_A4_inner[-1])\n",
"# add the vias to stitch them together\n",
"vias.append(create_via(coil_A1_A2_inner[0]))\n",
"vias.append(create_via(coil_A1_A2_inner[-1]))\n",
"vias.append(create_via(coil_A3_A4_inner[0]))\n",
"vias.append(create_via(coil_A3_A4_inner[-1]))\n",
"\n",
"# create tracks to link the B coils around the center - this can all be done on the bottom layer\n",
"coil_B1_B2_inner = draw_arc(1 * 360 / 12, 4 * 360 / 12, connection_radius1)\n",
"tracks_b.append(coil_B1_B2_inner)\n",
"coil_B3_B4_inner = draw_arc(7 * 360 / 12, 10 * 360 / 12, connection_radius1)\n",
"tracks_b.append(coil_B3_B4_inner)\n",
"# connect up the bottoms of the A coils\n",
"coils_b[1].append(coil_B1_B2_inner[0])\n",
"coils_b[4].append(coil_B1_B2_inner[-1])\n",
"coils_b[7].append(coil_B3_B4_inner[0])\n",
"coils_b[10].append(coil_B3_B4_inner[-1])\n",
"\n",
"# create tracks to link the C coils around the center\n",
"connection_via_radius_C = connection_via_radius_A + TRACK_SPACING + VIA_DIAM / 2\n",
"coil_C1_C2_inner = draw_arc(2 * 360 / 12, 5 * 360 / 12, connection_via_radius_C)\n",
"tracks_f.append(coil_C1_C2_inner)\n",
"coil_C3_C4_inner = draw_arc(8 * 360 / 12, 11 * 360 / 12, connection_via_radius_C)\n",
"tracks_f.append(coil_C3_C4_inner)\n",
"# connect up the bottoms of the B coils\n",
"coils_b[2].append(coil_C1_C2_inner[0])\n",
"coils_b[5].append(coil_C1_C2_inner[-1])\n",
"coils_b[8].append(coil_C3_C4_inner[0])\n",
"coils_b[11].append(coil_C3_C4_inner[-1])\n",
"# add the vias to stitch them together\n",
"vias.append(create_via(coil_C1_C2_inner[0]))\n",
"vias.append(create_via(coil_C1_C2_inner[-1]))\n",
"vias.append(create_via(coil_C3_C4_inner[0]))\n",
"vias.append(create_via(coil_C3_C4_inner[-1]))\n",
"\n",
"# connect the last three coils together\n",
"common_connection_radius = STATOR_RADIUS - TRACK_SPACING - TRACK_WIDTH\n",
"tracks_f.append(draw_arc(9 * 360 / 12, 11 * 360 / 12, common_connection_radius))\n",
"coils_f[9].append(get_arc_point(9 * 360 / 12, common_connection_radius))\n",
"coils_f[10].append(get_arc_point(10 * 360 / 12, common_connection_radius))\n",
"coils_f[11].append(get_arc_point(11 * 360 / 12, common_connection_radius))\n",
"\n",
"# connect the outer A coils together\n",
"outer_connection_radius_A = STATOR_RADIUS - TRACK_SPACING - TRACK_WIDTH\n",
"tracks_f.append(draw_arc(3 * 360 / 12, 6 * 360 / 12, outer_connection_radius_A))\n",
"coils_f[3].append(get_arc_point(3 * 360 / 12, outer_connection_radius_A))\n",
"coils_f[6].append(get_arc_point(6 * 360 / 12, outer_connection_radius_A))\n",
"\n",
"# connect the outer B coils together\n",
"outer_connection_radius_B = outer_connection_radius_A - TRACK_SPACING - VIA_DIAM / 2\n",
"tracks_b.append(\n",
" [get_arc_point(4 * 360 / 12, outer_connection_radius_B)]\n",
" + draw_arc(4 * 360 / 12, 7 * 360 / 12, outer_connection_radius_A)\n",
" + [get_arc_point(7 * 360 / 12, outer_connection_radius_B)]\n",
")\n",
"coils_f[4].append(get_arc_point(4 * 360 / 12, outer_connection_radius_B))\n",
"coils_f[7].append(get_arc_point(7 * 360 / 12, outer_connection_radius_B))\n",
"vias.append(create_via(get_arc_point(4 * 360 / 12, outer_connection_radius_B)))\n",
"vias.append(create_via(get_arc_point(7 * 360 / 12, outer_connection_radius_B)))\n",
"\n",
"# connect the outer C coilds together\n",
"outer_connection_radius_C = outer_connection_radius_B - TRACK_SPACING - VIA_DIAM / 2\n",
"tracks_b.append(draw_arc(5 * 360 / 12, 8 * 360 / 12, outer_connection_radius_C))\n",
"coils_f[5].append(get_arc_point(5 * 360 / 12, outer_connection_radius_C))\n",
"coils_f[8].append(get_arc_point(8 * 360 / 12, outer_connection_radius_C))\n",
"vias.append(create_via(get_arc_point(5 * 360 / 12, outer_connection_radius_C)))\n",
"vias.append(create_via(get_arc_point(8 * 360 / 12, outer_connection_radius_C)))\n",
"\n",
2022-10-12 09:33:06 +00:00
"# create pins for the input\n",
"# for angle in range(0, 360, 15):\n",
"# pins.append(create_pin(CONNECTION_PINS_RADIUS, angle, \"A\"))\n",
"\n",
"# coils_f[0].append(get_arc_point(0, CONNECTION_PINS_RADIUS))\n",
"# coils_f[1].append(get_arc_point(1 * 360 / 12, CONNECTION_PINS_RADIUS))\n",
"# coils_f[2].append(get_arc_point(2 * 360 / 12, CONNECTION_PINS_RADIUS))\n",
"\n",
"# coils_f[0].append(get_arc_point(15, CONNECTION_PINS_RADIUS))\n",
"# coils_f[1].append(get_arc_point(15 + 1 * 360 / 12, CONNECTION_PINS_RADIUS))\n",
2022-10-12 09:33:06 +00:00
"# coils_f[2].append(get_arc_point(15 + 2 * 360 / 12, CONNECTION_PINS_RADIUS))\n",
"\n",
"# create mounting holes at 45 degree angles\n",
"mounting_holes = [\n",
" create_mounting_hole(get_arc_point(angle, SCREW_HOLE_RADIUS), SCREW_HOLE_DRILL_DIAM)\n",
" for angle in [45, 135, 225, 315]\n",
"]\n",
"\n",
"# create the pads for connecting the inputs to the coils\n",
"pads.append(create_pad((INPUT_PAD_RADIUS, -PAD_PITCH), PAD_WIDTH, PAD_HEIGHT, \"b\"))\n",
"pads.append(create_pad((INPUT_PAD_RADIUS, 0), PAD_WIDTH, PAD_HEIGHT, \"b\"))\n",
2022-10-12 11:30:53 +00:00
"pads.append(create_pad((INPUT_PAD_RADIUS, PAD_PITCH), PAD_WIDTH, PAD_HEIGHT, \"b\"))\n",
"\n",
"# connect coil A to the top pad\n",
"coils_f[0].append((INPUT_PAD_RADIUS, -PAD_PITCH))\n",
"vias.append(create_via(((INPUT_PAD_RADIUS, -PAD_PITCH))))\n",
"# connect coil B to the middle pad\n",
"coils_f[1].append((INPUT_PAD_RADIUS, 0))\n",
"vias.append(create_via(((INPUT_PAD_RADIUS, 0))))\n",
"# connect coil C to the bottom pad\n",
"coils_f[2].append(\n",
" get_arc_point(\n",
" 2 * 360 / 12, SCREW_HOLE_RADIUS - (SCREW_HOLE_DRILL_DIAM / 2 + TRACK_SPACING)\n",
" )\n",
")\n",
"coils_f[2].append((INPUT_PAD_RADIUS, PAD_PITCH))\n",
"vias.append(create_via(((INPUT_PAD_RADIUS, PAD_PITCH))))"
2022-10-10 18:45:58 +00:00
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# dump out the json version\n",
"json_result = dump_json(\n",
" \"coils_12.json\",\n",
" STATOR_RADIUS,\n",
" STATOR_HOLE_RADIUS,\n",
" TRACK_WIDTH,\n",
2022-10-12 09:33:06 +00:00
" PIN_DIAM,\n",
" PIN_DRILL,\n",
" VIA_DIAM,\n",
" VIA_DRILL,\n",
" vias,\n",
2022-10-12 09:33:06 +00:00
" pins,\n",
" pads,\n",
" silk,\n",
" tracks_f,\n",
" tracks_b,\n",
2022-10-12 09:33:06 +00:00
" mounting_holes,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# plot the json\n",
"plot_json(json_result)"
2022-10-10 18:45:58 +00:00
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.10.7 ('venv': venv)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.7"
},
"vscode": {
"interpreter": {
"hash": "fc384f9db26c31784edfba3761ba3d2c7b2f9b8a63e03a9eb0778fc35334efe1"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}