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

580 lines
20 KiB
Text
Raw Normal View History

2022-11-19 08:24:27 +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",
" optimize_points,\n",
" chaikin,\n",
" rotate_point,\n",
" scale,\n",
")\n",
"from pcb_json import (\n",
" dump_json,\n",
" plot_json,\n",
" create_via,\n",
" create_pad,\n",
" create_pin,\n",
" create_track,\n",
" create_silk,\n",
" create_silk,\n",
" create_mounting_hole,\n",
")\n",
"\n",
"from enum import Enum\n",
"\n",
"Layer = Enum(\"Layer\", \"FRONT BACK\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Track width and spacing\n",
"TRACK_WIDTH = 0.127\n",
"TRACK_SPACING = 0.127\n",
"\n",
"# via defaults\n",
"VIA_DIAM = 0.8\n",
"VIA_DRILL = 0.4\n",
"\n",
"# this is for a 1.27mm pitch pin\n",
"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 = 3\n",
"PAD_HEIGHT = 2\n",
"PAD_PITCH = 2.5\n",
"\n",
"STATOR_HOLE_RADIUS = 5.5\n",
"HOLE_SPACING = 0.25"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Standard 25 mm version\n",
"\n",
"# PCB Edge size\n",
"STATOR_RADIUS = 25\n",
"STATOR_HOLE_RADIUS = 5.5\n",
"\n",
"# where to puth the mounting pins\n",
"SCREW_HOLE_DRILL_DIAM = 2.3 # 2.3mm drill for a 2mm screw\n",
"SCREW_HOLE_RADIUS = STATOR_RADIUS\n",
"\n",
"# Coil params\n",
"TURNS = 12\n",
"COIL_CENTER_RADIUS = 16\n",
"COIL_VIA_RADIUS = 17"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Large 30 mm version\n",
"\n",
"# PCB Edge size\n",
"STATOR_RADIUS = 30\n",
"\n",
"# where to puth the mounting pins\n",
"SCREW_HOLE_DRILL_DIAM = 2.3 # 2.3mm drill for a 2mm screw\n",
"SCREW_HOLE_RADIUS = STATOR_RADIUS\n",
"\n",
"# Coil params\n",
"TURNS = 16\n",
"COIL_CENTER_RADIUS = 19.95\n",
"COIL_VIA_RADIUS = 20.95"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# where to put the input pads\n",
"INPUT_PAD_RADIUS = STATOR_RADIUS - (PAD_WIDTH / 2 + VIA_DIAM + TRACK_SPACING)\n",
"\n",
"USE_SPIRAL = False\n",
"\n",
"LAYERS = 4"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Radial coil generator"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_points(spacing, inner_radius, outer_radius, start_angle, end_angle):\n",
" # first calculate the angle step size from the spacing and the inner_radius\n",
" spacing_angle = np.rad2deg(np.arctan2(spacing, inner_radius))\n",
" print(spacing_angle)\n",
" # now calculate the points be iterating from start_angle to end_angle with the spacing_angle\n",
" points = []\n",
" for angle in np.arange(start_angle, end_angle, spacing_angle * 2):\n",
" points.append(get_arc_point(angle, inner_radius))\n",
" points.append(get_arc_point(angle, outer_radius))\n",
" points.append(\n",
" (\n",
" get_arc_point(angle, outer_radius)[0],\n",
" get_arc_point(angle, outer_radius)[1] + spacing,\n",
" )\n",
" )\n",
" points.append(get_arc_point(angle + spacing_angle, inner_radius))\n",
" return points"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"points_f = get_points(\n",
" TRACK_SPACING + TRACK_WIDTH,\n",
" (COIL_CENTER_RADIUS - 10),\n",
" COIL_CENTER_RADIUS + 10,\n",
" -15,\n",
" 15,\n",
")\n",
"\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",
"\n",
"# 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)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"spacing = TRACK_SPACING + TRACK_WIDTH\n",
"inner_radius = COIL_CENTER_RADIUS - 10\n",
"outer_radius = COIL_CENTER_RADIUS + 10\n",
"start_angle = -15\n",
"end_angle = 15\n",
"# first calculate the angle step size from the spacing and the inner_radius\n",
"spacing_angle = np.rad2deg(np.arctan2(spacing, inner_radius))\n",
"# now calculate the points be iterating from start_angle to end_angle with the spacing_angle\n",
"i = 0\n",
"count = (end_angle - start_angle) // spacing_angle\n",
"print(spacing_angle, count)\n",
"# for angle in np.arange(start_angle, end_angle, spacing_angle):\n",
"for angle in [start_angle, end_angle]:\n",
" i = i + 1\n",
" current = 1\n",
" if i == 2:\n",
" current = -1\n",
" with open(f\"simulations/radial_{i}.csv\", \"w\") as f:\n",
" points = [\n",
" get_arc_point(angle, inner_radius),\n",
" get_arc_point(angle, outer_radius),\n",
" ]\n",
" points = translate(rotate(points, 90), -22.5, 90)\n",
" for point in points:\n",
" f.write(f\"{point[0]}/10,{point[1]}/10,0,{current}\\n\")\n",
"\n",
"\n",
"# # write the coil out in a format that can be simulated\n",
"p_f = translate(rotate(points_f, 90), -22.5, 90)\n",
"p_b = translate(rotate(points_f, 90), -22.5, 90)\n",
"\n",
"# df = pd.DataFrame(p_f, columns=[\"x\", \"y\"])\n",
"# ax = df.plot.line(x=\"x\", y=\"y\", color=\"blue\")\n",
"# ax.axis(\"equal\")\n",
"\n",
"with open(\"simulations/coils/coil_rad.csv\", \"w\") as f:\n",
" for point in p_f:\n",
" f.write(f\"{point[0]},{point[1]},0,0.5\\n\")\n",
"\n",
"# two layer board\n",
"with open(\"simulations/coils/coil-rad-2-layer.csv\", \"wt\") as f:\n",
" for point in p_f:\n",
" f.write(f\"{point[0]},{point[1]},0,0.5\\n\")\n",
" for point in p_b:\n",
" f.write(f\"{point[0]},{point[1]},{0-0.062},0.5\\n\")\n",
"\n",
"# all four layer board\n",
"with open(\"simulations/coils/coil-rad-4-layer.csv\", \"wt\") as f:\n",
" for point in p_f:\n",
" f.write(f\"{point[0]},{point[1]},0,0.5\\n\")\n",
" for point in p_b:\n",
" f.write(f\"{point[0]},{point[1]},{0-0.011},0.5\\n\")\n",
" for point in p_f:\n",
" f.write(f\"{point[0]},{point[1]},{0-(0.011+0.04)},0.5\\n\")\n",
" for point in p_b:\n",
" f.write(f\"{point[0]},{point[1]},{0-(0.011+0.011+0.04)},0.5\\n\")"
]
},
{
"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",
"pins = []\n",
"mounting_holes = []\n",
"silk = []\n",
"\n",
"\n",
"# shift the coils aronnd to make connections a bit easier\n",
"COIL_ROTATION = -360 / 12\n",
"\n",
"coil_angles = []\n",
"for i in range(12):\n",
" angle = i * 360 / 12 + COIL_ROTATION\n",
" coil_angles.append(angle)\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 = coil_angles[i]\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",
" # slightly nudge the coils so that they don't overlap when flipped\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 + 3 * TRACK_SPACING\n",
"\n",
"# create tracks to link the A coils around the center\n",
"connection_via_radius_A = connection_radius1 + 3 * TRACK_SPACING + VIA_DIAM / 2\n",
"coil_A1_A2_inner = (\n",
" [get_arc_point(coil_angles[0], connection_via_radius_A)]\n",
" + draw_arc(COIL_ROTATION, coil_angles[3], connection_radius1)\n",
" + [get_arc_point(coil_angles[3], connection_via_radius_A)]\n",
")\n",
"tracks_f.append(coil_A1_A2_inner)\n",
"coil_A3_A4_inner = (\n",
" [get_arc_point(coil_angles[6], connection_via_radius_A)]\n",
" + draw_arc(coil_angles[6], coil_angles[9], connection_radius1)\n",
" + [get_arc_point(coil_angles[9], 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(coil_angles[1], coil_angles[4], connection_radius1)\n",
"tracks_b.append(coil_B1_B2_inner)\n",
"coil_B3_B4_inner = draw_arc(coil_angles[7], coil_angles[10], 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 + 3 * TRACK_SPACING + VIA_DIAM / 2\n",
"coil_C1_C2_inner = draw_arc(coil_angles[2], coil_angles[5], connection_via_radius_C)\n",
"tracks_f.append(coil_C1_C2_inner)\n",
"coil_C3_C4_inner = draw_arc(coil_angles[8], coil_angles[11], 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 = SCREW_HOLE_RADIUS - (SCREW_HOLE_DRILL_DIAM / 2 + 0.5)\n",
"tracks_f.append(draw_arc(coil_angles[9], coil_angles[11], common_connection_radius))\n",
"coils_f[9].append(get_arc_point(coil_angles[9], common_connection_radius))\n",
"coils_f[10].append(get_arc_point(coil_angles[10], common_connection_radius))\n",
"coils_f[11].append(get_arc_point(coil_angles[11], common_connection_radius))\n",
"\n",
"# connect the outer A coils together\n",
"outer_connection_radius_A = SCREW_HOLE_RADIUS - (SCREW_HOLE_DRILL_DIAM / 2 + 0.5)\n",
"tracks_f.append(draw_arc(coil_angles[3], coil_angles[6], outer_connection_radius_A))\n",
"coils_f[3].append(get_arc_point(coil_angles[3], outer_connection_radius_A))\n",
"coils_f[6].append(get_arc_point(coil_angles[6], 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(coil_angles[4], outer_connection_radius_B)]\n",
" + draw_arc(coil_angles[4], coil_angles[7], outer_connection_radius_A)\n",
" + [get_arc_point(coil_angles[7], outer_connection_radius_B)]\n",
")\n",
"coils_f[4].append(get_arc_point(coil_angles[4], outer_connection_radius_B))\n",
"coils_f[7].append(get_arc_point(coil_angles[7], outer_connection_radius_B))\n",
"vias.append(\n",
" create_via(get_arc_point(4 * 360 / 12 + COIL_ROTATION, outer_connection_radius_B))\n",
")\n",
"vias.append(\n",
" create_via(get_arc_point(7 * 360 / 12 + COIL_ROTATION, outer_connection_radius_B))\n",
")\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(\n",
" draw_arc(\n",
" 5 * 360 / 12 + COIL_ROTATION,\n",
" 8 * 360 / 12 + COIL_ROTATION,\n",
" outer_connection_radius_C,\n",
" )\n",
")\n",
"coils_f[5].append(\n",
" get_arc_point(5 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C)\n",
")\n",
"coils_f[8].append(\n",
" get_arc_point(8 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C)\n",
")\n",
"vias.append(\n",
" create_via(get_arc_point(5 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C))\n",
")\n",
"vias.append(\n",
" create_via(get_arc_point(8 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C))\n",
")\n",
"\n",
"# create the pads for connecting the inputs to the coils\n",
"silk.append(\n",
" create_silk((INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, PAD_PITCH), \"C\", \"b\", 2.5, -900)\n",
")\n",
"silk.append(create_silk((INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, 0), \"B\", \"b\", 2.5, -900))\n",
"silk.append(\n",
" create_silk((INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, -PAD_PITCH), \"A\", \"b\", 2.5, -900)\n",
")\n",
"\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",
"pads.append(create_pad((INPUT_PAD_RADIUS, PAD_PITCH), PAD_WIDTH, PAD_HEIGHT, \"b\"))\n",
"\n",
"# connect coil A to the top pad\n",
"pad_connection_point_x = INPUT_PAD_RADIUS\n",
"pad_angle = np.rad2deg(np.arcsin(PAD_PITCH / pad_connection_point_x))\n",
"coils_f[0].append(get_arc_point(coil_angles[0], pad_connection_point_x))\n",
"vias.append(create_via(get_arc_point(coil_angles[0], pad_connection_point_x)))\n",
"# connect coil B to the middle pad\n",
"coils_f[1].append((pad_connection_point_x + PAD_WIDTH / 2 + VIA_DIAM / 2, 0))\n",
"vias.append(create_via(((pad_connection_point_x + PAD_WIDTH / 2 + VIA_DIAM / 2, 0))))\n",
"# connect coil C to the bottom pad\n",
"coils_f[2].append(get_arc_point(coil_angles[2], pad_connection_point_x))\n",
"vias.append(create_via(get_arc_point(coil_angles[2], pad_connection_point_x)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# if we are doing four layers then duplicate the front and back layers on (front and inner1), (inner2 and back)\n",
"tracks_in1 = []\n",
"tracks_in2 = []\n",
"if LAYERS == 4:\n",
" tracks_in1 = tracks_b.copy()\n",
" tracks_in2 = tracks_f.copy()\n",
"\n",
"# these final bits of wiring up to the input pads don't need to be duplicated\n",
"tracks_b.append(\n",
" [(pad_connection_point_x + PAD_WIDTH / 2, 0), (pad_connection_point_x, 0)]\n",
")\n",
"tracks_b.append(draw_arc(coil_angles[0], -pad_angle, pad_connection_point_x, 1))\n",
"tracks_b.append(draw_arc(coil_angles[2], pad_angle, pad_connection_point_x, 1))\n",
"\n",
"nibble_angle_size = 360 * SCREW_HOLE_DRILL_DIAM / (2 * np.pi * STATOR_RADIUS)\n",
"\n",
"outer_cuts = (\n",
" draw_arc(-45 + nibble_angle_size / 2, 45 - nibble_angle_size / 2, STATOR_RADIUS, 5)\n",
" + translate(\n",
" rotate(draw_arc(5, 175, SCREW_HOLE_DRILL_DIAM / 2, 5)[::-1], 135),\n",
" STATOR_RADIUS,\n",
" 45,\n",
" )\n",
" + draw_arc(\n",
" 45 + nibble_angle_size / 2, 135 - nibble_angle_size / 2, STATOR_RADIUS, 5\n",
" )\n",
" + translate(\n",
" rotate(draw_arc(5, 175, SCREW_HOLE_DRILL_DIAM / 2, 5), 225)[::-1],\n",
" STATOR_RADIUS,\n",
" 135,\n",
" )\n",
" + draw_arc(\n",
" 135 + nibble_angle_size / 2, 225 - nibble_angle_size / 2, STATOR_RADIUS, 5\n",
" )\n",
" + translate(\n",
" rotate(draw_arc(5, 175, SCREW_HOLE_DRILL_DIAM / 2, 5), 315)[::-1],\n",
" STATOR_RADIUS,\n",
" 225,\n",
" )\n",
" + draw_arc(\n",
" 225 + nibble_angle_size / 2, 315 - nibble_angle_size / 2, STATOR_RADIUS, 5\n",
" )\n",
" + translate(\n",
" rotate(draw_arc(5, 175, SCREW_HOLE_DRILL_DIAM / 2, 5), 45)[::-1],\n",
" STATOR_RADIUS,\n",
" 315,\n",
" )\n",
")\n",
"\n",
"edge_cuts = [\n",
" outer_cuts,\n",
" draw_arc(0, 360, STATOR_HOLE_RADIUS, 1),\n",
"]\n",
"\n",
"# dump out the json version\n",
"json_result = dump_json(\n",
" filename=f\"coils_12_{STATOR_RADIUS}mm.json\",\n",
" track_width=TRACK_WIDTH,\n",
" pin_diam=PIN_DIAM,\n",
" pin_drill=PIN_DRILL,\n",
" via_diam=VIA_DIAM,\n",
" via_drill=VIA_DRILL,\n",
" vias=vias,\n",
" pins=pins,\n",
" pads=pads,\n",
" silk=silk,\n",
" tracks_f=tracks_f,\n",
" tracks_in1=tracks_in1,\n",
" tracks_in2=tracks_in2,\n",
" tracks_b=tracks_b,\n",
" mounting_holes=mounting_holes,\n",
" edge_cuts=edge_cuts,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# plot the json\n",
"plot_json(json_result)"
]
}
],
"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": "1ce20143987840b9786ebb5907032c9c3a8efacbb887dbb0ebc4934f2ad26cb3"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}