Add coil to jupyter notebook

This commit is contained in:
Chris Greening 2022-10-08 17:59:50 +01:00
parent e2adb81c10
commit 01d08d02bb
3 changed files with 164 additions and 655 deletions

View file

@ -1,547 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib as plt\n",
"import scipy\n",
"from skspatial.objects import LineSegment\n",
"from enum import Enum"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"VIA_DIAM = 0.8\n",
"VIA_DRILL = 0.4\n",
"STATOR_HOLE_RADIUS = 4\n",
"TRACK_WIDTH = 0.2\n",
"TRACK_SPACING = 0.2\n",
"TURNS = 11\n",
"STATOR_RADIUS = 20\n",
"Layer = Enum(\"Layer\", \"FRONT BACK\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# create a square shape\n",
"# template = [(-1.5, -0.05), (1.5, -1.25), (1.5, 1.25), (-1.5, 0.05)]\n",
"# template = [(-6, -0.05), (6, -3), (6, 3), (-6, 0.05)]\n",
"# create a triangle\n",
"# template = [(-2, 0), (2, -3), (2, 3)]\n",
"# interpolate the shape using numpy\n",
"\n",
"# create a circle template\n",
"template = [\n",
" (np.cos(np.deg2rad(theta)), np.sin(np.deg2rad(theta)))\n",
" for theta in np.linspace(0, 360, 360)\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# plot the template shape wrapping around to the first point\n",
"plt.pyplot.plot(\n",
" [x for x, y in template] + [template[0][0]],\n",
" [y for x, y in template] + [template[0][1]],\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# apply Chaikin's algorithm to the template - corner cuttine\n",
"def chaikin(arr, iterations):\n",
" if iterations == 0:\n",
" return arr\n",
" l = len(arr)\n",
" smoothed = []\n",
" for i in range(l):\n",
" x1, y1 = arr[i]\n",
" x2, y2 = arr[(i + 1) % l]\n",
" smoothed.append([0.95 * x1 + 0.05 * x2, 0.95 * y1 + 0.05 * y2])\n",
" smoothed.append([0.05 * x1 + 0.95 * x2, 0.05 * y1 + 0.95 * y2])\n",
" return chaikin(smoothed, iterations - 1)\n",
"\n",
"\n",
"# template = chaikin(template, 2)\n",
"plt.pyplot.plot(\n",
" [x for x, y in template] + [template[0][0]],\n",
" [y for x, y in template] + [template[0][1]],\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# this speeds things up dramatically as we only have to compute the line intersections once\n",
"# there are probably much faster ways of doing this - we're just doing a brute force search\n",
"# for the intersections - consult algorithms from games for inspiration...\n",
"def get_template_point_cache(template):\n",
" # sweep a line from the origin through 360 degress times the number of turns in 1 degree increments\n",
" # and find the intersection points with the template shape\n",
" cache = {}\n",
" for angle in np.arange(0, 360 + 2, 2):\n",
" line = LineSegment(\n",
" np.array([0, 0]),\n",
" np.array(\n",
" [1000 * np.cos(np.deg2rad(angle)), 1000 * np.sin(np.deg2rad(angle))]\n",
" ),\n",
" )\n",
" for i in range(len(template)):\n",
" segment = LineSegment(\n",
" np.array(template[i]), np.array(template[(i + 1) % len(template)])\n",
" )\n",
" try:\n",
" intersection = line.intersect_line_segment(segment)\n",
" if intersection is not None:\n",
" cache[angle] = (intersection, segment)\n",
" except ValueError:\n",
" pass\n",
" return cache\n",
"\n",
"\n",
"# get the points in a coil shape\n",
"# Use reverse for bottom layer (basically flips the y coordinate so that the coil goes in the opposite direction)\n",
"# Also rotates the endpoints by 90 degress so that the exit point on the bottom layer is to the left hand side\n",
"def get_points(template, turns, spacing, layer=Layer.FRONT, cache=None):\n",
" if cache is None:\n",
" cache = get_template_point_cache(template)\n",
" points = []\n",
" for angle in np.arange(0, 360 * turns + 2, 2):\n",
" offset = spacing * angle / 360\n",
" if layer == Layer.BACK:\n",
" angle = angle + 180\n",
" intersection, segment = cache[angle % 360]\n",
" vector = np.array(segment.point_a) - np.array(segment.point_b)\n",
" normal = vector / np.linalg.norm(vector)\n",
" # rotate the vector 90 degrees\n",
" normal = np.array([-normal[1], normal[0]])\n",
" # move the intersection point along the normal vector by the spacing\n",
" coil_point = intersection + normal * offset\n",
" if layer == Layer.BACK:\n",
" points.append((coil_point[0], -coil_point[1]))\n",
" else:\n",
" points.append(coil_point)\n",
" return points"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cache = get_template_point_cache(template)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"points_f = [(0, 0)] + get_points(\n",
" template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT, cache\n",
")\n",
"points_b = [(0, 0)] + get_points(\n",
" template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, cache\n",
")\n",
"\n",
"COIL_CENTER_RADIUS = STATOR_RADIUS / 2 + 1.5\n",
"\n",
"angle_A = 0\n",
"angle_B = 120\n",
"angle_C = 240\n",
"\n",
"# roate the points by the required angle\n",
"def rotate(points, angle):\n",
" return [\n",
" [\n",
" x * np.cos(np.deg2rad(angle)) - y * np.sin(np.deg2rad(angle)),\n",
" x * np.sin(np.deg2rad(angle)) + y * np.cos(np.deg2rad(angle)),\n",
" ]\n",
" for x, y in points\n",
" ]\n",
"\n",
"\n",
"# move the points out to the distance at the requited angle\n",
"def translate(points, distance, angle):\n",
" return [\n",
" [\n",
" x + distance * np.cos(np.deg2rad(angle)),\n",
" y + distance * np.sin(np.deg2rad(angle)),\n",
" ]\n",
" for x, y in points\n",
" ]\n",
"\n",
"\n",
"# flip the y coordinate\n",
"def flip(points):\n",
" return [[x, -y] for x, y in points]\n",
"\n",
"\n",
"# the main coils\n",
"coil_A_f = translate(rotate(points_f, angle_A), COIL_CENTER_RADIUS, angle_A)\n",
"coil_A_b = translate(rotate(points_b, angle_A), COIL_CENTER_RADIUS, angle_A)\n",
"\n",
"coil_B_f = translate(rotate(points_f, angle_B), COIL_CENTER_RADIUS, angle_B)\n",
"coil_B_b = translate(rotate(points_b, angle_B), COIL_CENTER_RADIUS, angle_B)\n",
"\n",
"coil_C_f = translate(rotate(points_f, angle_C), COIL_CENTER_RADIUS, angle_C)\n",
"coil_C_b = translate(rotate(points_b, angle_C), COIL_CENTER_RADIUS, angle_C)\n",
"\n",
"# the opposite coils - for more power!\n",
"angle_A_opp = angle_A + 180\n",
"angle_B_opp = angle_B + 180\n",
"angle_C_opp = angle_C + 180\n",
"\n",
"print(angle_A_opp, angle_B_opp, angle_C_opp)\n",
"\n",
"coil_A_opp_f = translate(\n",
" rotate(flip(points_f), angle_A_opp), COIL_CENTER_RADIUS, angle_A_opp\n",
")\n",
"coil_A_opp_b = translate(\n",
" rotate(flip(points_b), angle_A_opp), COIL_CENTER_RADIUS, angle_A_opp\n",
")\n",
"\n",
"coil_B_opp_f = translate(\n",
" rotate(flip(points_f), angle_B_opp), COIL_CENTER_RADIUS, angle_B_opp\n",
")\n",
"coil_B_opp_b = translate(\n",
" rotate(flip(points_b), angle_B_opp), COIL_CENTER_RADIUS, angle_B_opp\n",
")\n",
"\n",
"coil_C_opp_f = translate(\n",
" rotate(flip(points_f), angle_C_opp), COIL_CENTER_RADIUS, angle_C_opp\n",
")\n",
"coil_C_opp_b = translate(\n",
" rotate(flip(points_b), angle_C_opp), COIL_CENTER_RADIUS, angle_C_opp\n",
")\n",
"\n",
"# connect the front copper opposite coils together\n",
"common_connection_radius = STATOR_RADIUS - (TRACK_WIDTH + TRACK_SPACING)\n",
"common_coil_connections_b = [\n",
" (\n",
" common_connection_radius * np.cos(np.deg2rad(angle)),\n",
" common_connection_radius * np.sin(np.deg2rad(angle)),\n",
" )\n",
" for angle in np.arange(angle_A_opp, angle_C_opp + 5, 5)\n",
"]\n",
"coil_A_opp_f.append(\n",
" (\n",
" common_connection_radius * np.cos(np.deg2rad(angle_A_opp)),\n",
" common_connection_radius * np.sin(np.deg2rad(angle_A_opp)),\n",
" )\n",
")\n",
"coil_B_opp_f.append(\n",
" (\n",
" common_connection_radius * np.cos(np.deg2rad(angle_B_opp)),\n",
" common_connection_radius * np.sin(np.deg2rad(angle_B_opp)),\n",
" )\n",
")\n",
"coil_C_opp_f.append(\n",
" (\n",
" common_connection_radius * np.cos(np.deg2rad(angle_C_opp)),\n",
" common_connection_radius * np.sin(np.deg2rad(angle_C_opp)),\n",
" )\n",
")\n",
"\n",
"# connect coil A to it's opposite\n",
"connection_radius1 = STATOR_HOLE_RADIUS + (TRACK_SPACING)\n",
"connection_radius2 = connection_radius1 + (TRACK_SPACING + VIA_DIAM / 2)\n",
"# draw a 45 degree line from coil A at connection radius 1\n",
"# then connect up to connection radius 2\n",
"# draw a 45 degree line to the opposite coil\n",
"coil_A_b.append(\n",
" (\n",
" connection_radius1 * np.cos(np.deg2rad(angle_A)),\n",
" connection_radius1 * np.sin(np.deg2rad(angle_A)),\n",
" )\n",
")\n",
"coil_A_opp_b.append(\n",
" (\n",
" connection_radius2 * np.cos(np.deg2rad(angle_A_opp)),\n",
" connection_radius2 * np.sin(np.deg2rad(angle_A_opp)),\n",
" )\n",
")\n",
"a_connection_b = [\n",
" (\n",
" connection_radius1 * np.cos(np.deg2rad(angle)),\n",
" connection_radius1 * np.sin(np.deg2rad(angle)),\n",
" )\n",
" for angle in np.arange(angle_A, angle_A + 90 + 5, 5)\n",
"]\n",
"a_connection_f = [\n",
" (\n",
" connection_radius2 * np.cos(np.deg2rad(angle)),\n",
" connection_radius2 * np.sin(np.deg2rad(angle)),\n",
" )\n",
" for angle in np.arange(angle_A + 90, angle_A + 180 + 5, 5)\n",
"]\n",
"a_connection_b.append(a_connection_f[0])\n",
"\n",
"coil_B_b.append(\n",
" (\n",
" connection_radius1 * np.cos(np.deg2rad(angle_B)),\n",
" connection_radius1 * np.sin(np.deg2rad(angle_B)),\n",
" )\n",
")\n",
"coil_B_opp_b.append(\n",
" (\n",
" connection_radius2 * np.cos(np.deg2rad(angle_B_opp)),\n",
" connection_radius2 * np.sin(np.deg2rad(angle_B_opp)),\n",
" )\n",
")\n",
"b_connection_b = [\n",
" (\n",
" connection_radius1 * np.cos(np.deg2rad(angle)),\n",
" connection_radius1 * np.sin(np.deg2rad(angle)),\n",
" )\n",
" for angle in np.arange(angle_B, angle_B + 90 + 5, 5)\n",
"]\n",
"b_connection_f = [\n",
" (\n",
" connection_radius2 * np.cos(np.deg2rad(angle)),\n",
" connection_radius2 * np.sin(np.deg2rad(angle)),\n",
" )\n",
" for angle in np.arange(angle_B + 90, angle_B + 180 + 5, 5)\n",
"]\n",
"b_connection_b.append(b_connection_f[0])\n",
"\n",
"coil_C_b.append(\n",
" (\n",
" connection_radius1 * np.cos(np.deg2rad(angle_C)),\n",
" connection_radius1 * np.sin(np.deg2rad(angle_C)),\n",
" )\n",
")\n",
"coil_C_opp_b.append(\n",
" (\n",
" connection_radius2 * np.cos(np.deg2rad(angle_C_opp)),\n",
" connection_radius2 * np.sin(np.deg2rad(angle_C_opp)),\n",
" )\n",
")\n",
"c_connection_b = [\n",
" (\n",
" connection_radius1 * np.cos(np.deg2rad(angle)),\n",
" connection_radius1 * np.sin(np.deg2rad(angle)),\n",
" )\n",
" for angle in np.arange(angle_C, angle_C + 90 + 5, 5)\n",
"]\n",
"c_connection_f = [\n",
" (\n",
" connection_radius2 * np.cos(np.deg2rad(angle)),\n",
" connection_radius2 * np.sin(np.deg2rad(angle)),\n",
" )\n",
" for angle in np.arange(angle_C + 90, angle_C + 180 + 5, 5)\n",
"]\n",
"c_connection_b.append(c_connection_f[0])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def create_track(points):\n",
" return [{\"x\": x, \"y\": y} for x, y in points]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# dump out the results to json\n",
"json_result = {\n",
" \"parameters\": {\n",
" \"trackWidth\": TRACK_WIDTH,\n",
" \"statorHoleRadius\": STATOR_HOLE_RADIUS,\n",
" \"viaDiameter\": VIA_DIAM,\n",
" \"viaDrillDiameter\": VIA_DRILL,\n",
" },\n",
" \"vias\": [\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_A)),\n",
" },\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_B)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_B)),\n",
" },\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_C)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_C)),\n",
" },\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A_opp)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_A_opp)),\n",
" },\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_B_opp)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_B_opp)),\n",
" },\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_C_opp)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_C_opp)),\n",
" },\n",
" {\n",
" \"x\": common_connection_radius * np.cos(np.deg2rad(angle_A_opp)),\n",
" \"y\": common_connection_radius * np.sin(np.deg2rad(angle_A_opp)),\n",
" },\n",
" {\n",
" \"x\": common_connection_radius * np.cos(np.deg2rad(angle_B_opp)),\n",
" \"y\": common_connection_radius * np.sin(np.deg2rad(angle_B_opp)),\n",
" },\n",
" {\n",
" \"x\": common_connection_radius * np.cos(np.deg2rad(angle_C_opp)),\n",
" \"y\": common_connection_radius * np.sin(np.deg2rad(angle_C_opp)),\n",
" },\n",
" # coil A connections\n",
" {\"x\": a_connection_f[0][0], \"y\": a_connection_f[0][1]},\n",
" {\"x\": a_connection_f[-1][0], \"y\": a_connection_f[-1][1]},\n",
" # coil B connections\n",
" {\"x\": b_connection_f[0][0], \"y\": b_connection_f[0][1]},\n",
" {\"x\": b_connection_f[-1][0], \"y\": b_connection_f[-1][1]},\n",
" # coil C connections\n",
" {\"x\": c_connection_f[0][0], \"y\": c_connection_f[0][1]},\n",
" {\"x\": c_connection_f[-1][0], \"y\": c_connection_f[-1][1]},\n",
" ],\n",
" \"silk\": [\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_A)),\n",
" \"text\": \"A\",\n",
" },\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_B)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_B)),\n",
" \"text\": \"B\",\n",
" },\n",
" {\n",
" \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_C)),\n",
" \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_C)),\n",
" \"text\": \"C\",\n",
" },\n",
" ],\n",
" \"tracks\": {\n",
" \"f\": [\n",
" create_track(points)\n",
" for points in [\n",
" coil_A_f,\n",
" coil_A_opp_f,\n",
" coil_B_f,\n",
" coil_B_opp_f,\n",
" coil_C_f,\n",
" coil_C_opp_f,\n",
" a_connection_f,\n",
" b_connection_f,\n",
" c_connection_f,\n",
" ]\n",
" ],\n",
" \"b\": [\n",
" create_track(points)\n",
" for points in [\n",
" coil_A_b,\n",
" coil_A_opp_b,\n",
" coil_B_b,\n",
" coil_B_opp_b,\n",
" coil_C_b,\n",
" coil_C_opp_b,\n",
" common_coil_connections_b,\n",
" a_connection_b,\n",
" b_connection_b,\n",
" c_connection_b,\n",
" ]\n",
" ],\n",
" },\n",
"}\n",
"\n",
"import json\n",
"\n",
"json.dump(json_result, open(\"coil.json\", \"w\"))\n",
"\n",
"\n",
"df = pd.DataFrame(coil_A_f, columns=[\"x\", \"y\"])\n",
"ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n",
"ax.axis(\"equal\")\n",
"df = pd.DataFrame(coil_A_b, columns=[\"x\", \"y\"])\n",
"ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil B\", color=\"green\")\n",
"ax.axis(\"equal\")\n",
"\n",
"# plot all three coils on the same graph\n",
"# df = pd.DataFrame(coil_A, columns=['x', 'y'])\n",
"# ax = df.plot.line(x='x', y='y', label='Coil A', color='blue')\n",
"# ax.axis('equal')\n",
"# df = pd.DataFrame(coil_B, columns=['x', 'y'])\n",
"# df.plot.line(x='x', y='y', ax=ax, label='Coil B', color='green')\n",
"# df = pd.DataFrame(coil_C, columns=['x', 'y'])\n",
"# df.plot.line(x='x', y='y', ax=ax, label='Coil C', color='red')\n",
"\n",
"# df = pd.DataFrame(coil_A_opposite, columns=['x', 'y'])\n",
"# df.plot.line(x='x', y='y', ax=ax, label='Coil A Opposite', color='blue')\n",
"# df = pd.DataFrame(coil_B_opposite, columns=['x', 'y'])\n",
"# df.plot.line(x='x', y='y', ax=ax, label='Coil B Opposite', color='green')\n",
"# df = pd.DataFrame(coil_C_opposite, columns=['x', 'y'])\n",
"# df.plot.line(x='x', y='y', ax=ax, label='Coil C Opposite', color='red')"
]
}
],
"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
}

View file

@ -1,5 +1,6 @@
import pcbnew import pcbnew
import json import json
import wx
def create_tracks(board, group, net, layer, thickness, coords): def create_tracks(board, group, net, layer, thickness, coords):
@ -31,65 +32,72 @@ class CoilPlugin(pcbnew.ActionPlugin):
# self.icon_file_name = os.path.join(os.path.dirname(__file__), 'simple_plugin.png') # Optional, defaults to "" # self.icon_file_name = os.path.join(os.path.dirname(__file__), 'simple_plugin.png') # Optional, defaults to ""
def Run(self): def Run(self):
board = pcbnew.GetBoard() # launch a file picker dialog to get the coil file
# load up the JSON with the coil parameters dialog = wx.FileDialog(None, "Choose a coil file", "", "", "*.json", wx.FD_OPEN)
coil_data = json.load( if dialog.ShowModal() == wx.ID_OK:
open( # read the file
"/Users/chrisgreening/Work/projects/pcb_motor/kicad-coil-plugins/coil.json" with open(dialog.GetPath(), "r") as f:
) board = pcbnew.GetBoard()
) # load up the JSON with the coil parameters
# parameters coil_data = json.load(f)
track_width = coil_data["parameters"]["trackWidth"] # parameters
stator_hole_radius = coil_data["parameters"]["statorHoleRadius"] track_width = coil_data["parameters"]["trackWidth"]
via_diameter = coil_data["parameters"]["viaDiameter"] stator_hole_radius = coil_data["parameters"]["statorHoleRadius"]
via_drill_diameter = coil_data["parameters"]["viaDrillDiameter"] via_diameter = coil_data["parameters"]["viaDiameter"]
via_drill_diameter = coil_data["parameters"]["viaDrillDiameter"]
# put everything in a group to make it easier to manage # put everything in a group to make it easier to manage
pcb_group = pcbnew.PCB_GROUP(board) pcb_group = pcbnew.PCB_GROUP(board)
# board.Add(pcb_group) # board.Add(pcb_group)
# create the center hole # create the center hole
arc = pcbnew.PCB_SHAPE(board) arc = pcbnew.PCB_SHAPE(board)
arc.SetShape(pcbnew.SHAPE_T_ARC) arc.SetShape(pcbnew.SHAPE_T_ARC)
arc.SetStart(pcbnew.wxPointMM(stator_hole_radius, 0)) arc.SetStart(pcbnew.wxPointMM(stator_hole_radius, 0))
arc.SetCenter(pcbnew.wxPointMM(0, 0)) arc.SetCenter(pcbnew.wxPointMM(0, 0))
arc.SetArcAngleAndEnd(0, False) arc.SetArcAngleAndEnd(0, False)
arc.SetLayer(pcbnew.Edge_Cuts) arc.SetLayer(pcbnew.Edge_Cuts)
arc.SetWidth(int(0.1 * pcbnew.IU_PER_MM)) arc.SetWidth(int(0.1 * pcbnew.IU_PER_MM))
board.Add(arc) board.Add(arc)
# pcb_group.AddItem(arc) # pcb_group.AddItem(arc)
# create tracks # create tracks
for track in coil_data["tracks"]["f"]: for track in coil_data["tracks"]["f"]:
# find the matching net for the track # find the matching net for the track
net = board.FindNet("coils") net = board.FindNet("coils")
if net is None: if net is None:
raise "Net not found: {}".format(track["net"]) raise "Net not found: {}".format(track["net"])
create_tracks(board, pcb_group, net, pcbnew.F_Cu, track_width, track) create_tracks(
board, pcb_group, net, pcbnew.F_Cu, track_width, track
)
for track in coil_data["tracks"]["b"]: for track in coil_data["tracks"]["b"]:
create_tracks(board, pcb_group, net, pcbnew.B_Cu, track_width, track) create_tracks(
board, pcb_group, net, pcbnew.B_Cu, track_width, track
)
# create the vias # create the vias
for via in coil_data["vias"]: for via in coil_data["vias"]:
pcb_via = pcbnew.PCB_VIA(board) pcb_via = pcbnew.PCB_VIA(board)
pcb_via.SetPosition(pcbnew.wxPointMM(float(via["x"]), float(via["y"]))) pcb_via.SetPosition(
pcb_via.SetWidth(int(via_diameter * 1e6)) pcbnew.wxPointMM(float(via["x"]), float(via["y"]))
pcb_via.SetDrill(int(via_drill_diameter * 1e6)) )
pcb_via.SetNetCode(net.GetNetCode()) pcb_via.SetWidth(int(via_diameter * 1e6))
board.Add(pcb_via) pcb_via.SetDrill(int(via_drill_diameter * 1e6))
# pcb_group.AddItem(pcb_via) pcb_via.SetNetCode(net.GetNetCode())
board.Add(pcb_via)
# pcb_group.AddItem(pcb_via)
# create any silk screen # create any silk screen
for text in coil_data["silk"]: for text in coil_data["silk"]:
pcb_txt = pcbnew.PCB_TEXT(board) pcb_txt = pcbnew.PCB_TEXT(board)
pcb_txt.SetText(text["text"]) pcb_txt.SetText(text["text"])
pcb_txt.SetPosition(pcbnew.wxPointMM(text["x"], text["y"])) pcb_txt.SetPosition(pcbnew.wxPointMM(text["x"], text["y"]))
pcb_txt.SetHorizJustify(pcbnew.GR_TEXT_HJUSTIFY_CENTER) pcb_txt.SetHorizJustify(pcbnew.GR_TEXT_HJUSTIFY_CENTER)
pcb_txt.SetTextSize(pcbnew.wxSize(5000000, 5000000)) pcb_txt.SetTextSize(pcbnew.wxSize(5000000, 5000000))
pcb_txt.SetLayer(pcbnew.F_SilkS) pcb_txt.SetLayer(pcbnew.F_SilkS)
board.Add(pcb_txt) board.Add(pcb_txt)
# pcb_group.AddItem(pcb_txt) # pcb_group.AddItem(pcb_txt)
CoilPlugin().register() # Instantiate and register to Pcbnew]) CoilPlugin().register() # Instantiate and register to Pcbnew])

View file

@ -29,9 +29,17 @@
"TRACK_SPACING = 0.2\n", "TRACK_SPACING = 0.2\n",
"TURNS = 11\n", "TURNS = 11\n",
"STATOR_RADIUS = 20\n", "STATOR_RADIUS = 20\n",
"USE_SPIRAL = False\n",
"Layer = Enum(\"Layer\", \"FRONT BACK\")" "Layer = Enum(\"Layer\", \"FRONT BACK\")"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Arbitrary Coil Generation"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
@ -200,23 +208,6 @@
" return chaikin(smoothed, iterations - 1)" " return chaikin(smoothed, iterations - 1)"
] ]
}, },
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"points = get_points(template, 10, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, None)\n",
"optimized_points = optimize_points(points)\n",
"df = pd.DataFrame(points, columns=[\"x\", \"y\"])\n",
"ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n",
"ax.axis(\"equal\")\n",
"\n",
"df_optim = pd.DataFrame(optimized_points, columns=[\"x\", \"y\"])\n",
"df_optim.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"green\", ax=ax)\n",
"print(len(df_optim), len(df))"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
@ -232,15 +223,84 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"vias = []\n", "if not USE_SPIRAL:\n",
" points_f = get_points(\n",
" template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT, cache\n",
" )\n",
" points_b = get_points(\n",
" template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, cache\n",
" )\n",
"\n", "\n",
"points_f = get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT, cache)\n", " points_f = [(0, 0)] + chaikin(optimize_points(points_f), 3)\n",
"points_b = get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, cache)\n", " points_b = [(0, 0)] + chaikin(optimize_points(points_b), 3)\n",
"\n", " print(\"Track points\", len(points_f), len(points_b))\n",
"points_f = [(0, 0)] + chaikin(optimize_points(points_f), 3)\n", "else:\n",
"points_b = [(0, 0)] + chaikin(optimize_points(points_b), 3)\n", " print(\"Using spiral\")"
"print(\"Track points\", len(points_f), len(points_b))\n", ]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Basic Spiral Coil Generation"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_spiral(turns, start_radius, thickness, layer=Layer.FRONT):\n",
" points = []\n",
" # create a starting point in the center\n",
" for angle in np.arange(0, turns * 360, 1):\n",
" radius = start_radius + thickness * angle / 360\n",
" if layer == Layer.BACK:\n",
" x = radius * np.cos(np.deg2rad(angle + 180))\n",
" y = radius * np.sin(np.deg2rad(angle + 180))\n",
" points.append((x, -y))\n",
" else:\n",
" x = radius * np.cos(np.deg2rad(angle))\n",
" y = radius * np.sin(np.deg2rad(angle))\n",
" points.append((x, y))\n",
" return points"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"if USE_SPIRAL:\n",
" points_f = get_spiral(\n",
" TURNS, VIA_DIAM + TRACK_SPACING, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT\n",
" )\n",
" points_b = get_spiral(\n",
" TURNS, VIA_DIAM + TRACK_SPACING, TRACK_SPACING + TRACK_WIDTH, Layer.BACK\n",
" )\n",
"\n", "\n",
" points_f = [(0, 0)] + points_f\n",
" points_b = [(0, 0)] + points_b\n",
" print(\"Track points\", len(points_f), len(points_b))\n",
"else:\n",
" print(\"Using template\")"
]
},
{
"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", "# calculat the total length of the track to compute the resistance\n",
"total_length_front = 0\n", "total_length_front = 0\n",
"for i in range(len(points_f) - 1):\n", "for i in range(len(points_f) - 1):\n",
@ -254,8 +314,16 @@
" total_length_back += np.linalg.norm(\n", " total_length_back += np.linalg.norm(\n",
" np.array(points_b[i + 1]) - np.array(points_b[i])\n", " np.array(points_b[i + 1]) - np.array(points_b[i])\n",
" )\n", " )\n",
"print(\"Total length back\", total_length_back)\n", "print(\"Total length back\", total_length_back)"
"\n", ]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vias = []\n",
"\n", "\n",
"COIL_CENTER_RADIUS = STATOR_RADIUS / 2 + 1.5\n", "COIL_CENTER_RADIUS = STATOR_RADIUS / 2 + 1.5\n",
"\n", "\n",
@ -327,8 +395,6 @@
"angle_B_opp = angle_B + 180\n", "angle_B_opp = angle_B + 180\n",
"angle_C_opp = angle_C + 180\n", "angle_C_opp = angle_C + 180\n",
"\n", "\n",
"print(angle_A_opp, angle_B_opp, angle_C_opp)\n",
"\n",
"coil_A_opp_f = translate(\n", "coil_A_opp_f = translate(\n",
" rotate(flip(points_f), angle_A_opp), COIL_CENTER_RADIUS, angle_A_opp\n", " rotate(flip(points_f), angle_A_opp), COIL_CENTER_RADIUS, angle_A_opp\n",
")\n", ")\n",
@ -428,15 +494,9 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"def create_track(points):\n", "def create_track(points):\n",
" return [{\"x\": x, \"y\": y} for x, y in points]" " return [{\"x\": x, \"y\": y} for x, y in points]\n",
] "\n",
}, "\n",
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# dump out the results to json\n", "# dump out the results to json\n",
"json_result = {\n", "json_result = {\n",
" \"parameters\": {\n", " \"parameters\": {\n",
@ -504,24 +564,12 @@
"json.dump(json_result, open(\"coil.json\", \"w\"))\n", "json.dump(json_result, open(\"coil.json\", \"w\"))\n",
"\n", "\n",
"\n", "\n",
"df = pd.DataFrame(coil_A_f, columns=[\"x\", \"y\"])\n", "# df = pd.DataFrame(coil_A_f, columns=[\"x\", \"y\"])\n",
"ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n", "# ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n",
"ax.axis(\"equal\")\n", "# ax.axis(\"equal\")\n",
"df = pd.DataFrame(coil_A_b, columns=[\"x\", \"y\"])\n", "# df = pd.DataFrame(coil_A_b, columns=[\"x\", \"y\"])\n",
"ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil B\", color=\"green\")\n", "# ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil B\", color=\"green\")\n",
"ax.axis(\"equal\")\n", "# ax.axis(\"equal\")\n",
"\n",
"# print the number of segments in each front track\n",
"for track in json_result[\"tracks\"][\"f\"]:\n",
" print(len(track))\n",
"\n",
"# print the number of segments in each back track\n",
"for track in json_result[\"tracks\"][\"b\"]:\n",
" print(len(track))\n",
"\n",
"# print the total number of segments in both front and back tracks\n",
"print(sum([len(track) for track in json_result[\"tracks\"][\"f\"]]))\n",
"print(sum([len(track) for track in json_result[\"tracks\"][\"b\"]]))\n",
"\n", "\n",
"\n", "\n",
"# plot the back tracks\n", "# plot the back tracks\n",