"""Circular integrated controller PCB, power stages, and rear terminals.""" from __future__ import annotations import math import simplecadapi as scad try: from .common import ( apply_tags, connector_ref, ground_constraint_report, make_axial_hole_cutters_rsolids, make_axis_part_rpart, radial_centers, ) from .dimensions import ( PCB_BOTTOM_Z, PCB_CENTER_BORE_RADIUS, PCB_MOUNT_HOLE_RADIUS, PCB_RADIUS, PCB_STANDOFF_PCD, PCB_THICKNESS, REAR_COLUMN_PCD, ) except ImportError: # Support direct execution from this example directory. from common import ( apply_tags, connector_ref, ground_constraint_report, make_axial_hole_cutters_rsolids, make_axis_part_rpart, radial_centers, ) from dimensions import ( PCB_BOTTOM_Z, PCB_CENTER_BORE_RADIUS, PCB_MOUNT_HOLE_RADIUS, PCB_RADIUS, PCB_STANDOFF_PCD, PCB_THICKNESS, REAR_COLUMN_PCD, ) PHASE_TERMINAL_CENTER = (-11.0, 0.0) POWER_CAN_TERMINAL_CENTER = (11.0, 0.0) MOSFET_ANGLES = (22.5, 67.5, 112.5, 202.5, 247.5, 292.5) def make_integrated_controller_rassembly( *, pcb_material: scad.Material, terminal_material: scad.Material, ) -> scad.Assembly: """Build the circular ESC with six power devices and two service terminals.""" pcb = _make_controller_pcb_rpart(material=pcb_material) mosfet = _make_mosfet_package_rpart(material=terminal_material) phase_terminal = _make_terminal_block_rpart( part_id="three_phase_terminal", name="Three-position motor phase terminal", width=8.0, pin_count=3, material=terminal_material, ) power_terminal = _make_terminal_block_rpart( part_id="power_can_terminal", name="Four-position DC power and CAN terminal", width=8.0, pin_count=4, material=terminal_material, ) controller = scad.make_assembly_rassembly( assembly_id="integrated_circular_motor_controller", name="44.4 mm circular integrated BLDC controller", ) controller = scad.add_component_rassembly( assembly=controller, item=pcb, component_id="pcb", placement=scad.identity_placement_rplacement(), name="Circular controller PCB", ) controller = scad.ground_component_rassembly(assembly=controller, component_id="pcb") for index, angle in enumerate(MOSFET_ANGLES): radians = math.radians(angle) center = (13.2 * math.cos(radians), 13.2 * math.sin(radians)) component_id = f"mosfet_{index + 1}" z = PCB_BOTTOM_Z + PCB_THICKNESS controller = scad.add_component_rassembly( assembly=controller, item=mosfet, component_id=component_id, placement=scad.make_placement_rplacement(origin=(center[0], center[1], z)), name=f"Power MOSFET package {index + 1}", ) controller = scad.add_fixed_constraint_rassembly( assembly=controller, constraint_id=f"{component_id}_soldered", connector_a=connector_ref(component_id="pcb", connector_id=component_id), connector_b=connector_ref(component_id=component_id, connector_id="solder_axis"), name=f"MOSFET {index + 1} solder attachment", ) for component_id, item, center, connector_id in ( ("phase_terminal", phase_terminal, PHASE_TERMINAL_CENTER, "phase_terminal"), ("power_can_terminal", power_terminal, POWER_CAN_TERMINAL_CENTER, "power_can_terminal"), ): controller = scad.add_component_rassembly( assembly=controller, item=item, component_id=component_id, placement=scad.make_placement_rplacement(origin=(center[0], center[1], -38.5)), name=item.name, ) controller = scad.add_fixed_constraint_rassembly( assembly=controller, constraint_id=f"{component_id}_soldered", connector_a=connector_ref(component_id="pcb", connector_id=connector_id), connector_b=connector_ref(component_id=component_id, connector_id="solder_axis"), name=f"{component_id.replace('_', ' ')} solder and screw retention", ) for connector_id in ("cover_axis", "phase_access", "power_can_access"): controller = scad.forward_connector_rassembly( assembly=controller, connector_id=connector_id, source_component_id="pcb", source_connector_id=connector_id, name=connector_id.replace("_", " "), offset=None, ) controller = scad.solve_assembly_constraints_rassembly(assembly=controller, strict=True) ground_constraint_report(label="controller", assembly=controller) print("controller_packaging: pcb_d=44.4 mosfets=6 phase_pins=3 power_can_pins=4") return controller def _make_controller_pcb_rpart(*, material: scad.Material) -> scad.Part: board = scad.make_cylinder_rsolid( radius=PCB_RADIUS, height=PCB_THICKNESS, bottom_face_center=(0.0, 0.0, PCB_BOTTOM_Z), axis=(0.0, 0.0, 1.0), ) cutters: list[scad.Solid] = [ scad.make_cylinder_rsolid( radius=PCB_CENTER_BORE_RADIUS, height=PCB_THICKNESS + 2.0, bottom_face_center=(0.0, 0.0, PCB_BOTTOM_Z - 1.0), axis=(0.0, 0.0, 1.0), ) ] cutters.extend( make_axial_hole_cutters_rsolids( count=4, pcd=PCB_STANDOFF_PCD, hole_radius=PCB_MOUNT_HOLE_RADIUS, bottom_z=PCB_BOTTOM_Z - 1.0, height=PCB_THICKNESS + 2.0, angle_offset=45.0, ) ) cutters.extend( make_axial_hole_cutters_rsolids( count=4, pcd=REAR_COLUMN_PCD, hole_radius=3.45, bottom_z=PCB_BOTTOM_Z - 1.0, height=PCB_THICKNESS + 2.0, ) ) for x, count in ((PHASE_TERMINAL_CENTER[0], 3), (POWER_CAN_TERMINAL_CENTER[0], 4)): for pin in range(count): y = (pin - (count - 1) / 2.0) * 1.8 cutters.append( scad.make_cylinder_rsolid( radius=0.65, height=PCB_THICKNESS + 2.0, bottom_face_center=(x, y, PCB_BOTTOM_Z - 1.0), axis=(0.0, 0.0, 1.0), ) ) board = scad.cut_rsolid(board, cutters, skip_non_intersecting=False) board = apply_tags( shape=board, tags=("role.circular_esc_pcb", "role.controller_mounting_holes", "group.integrated_electronics"), ) connectors = [ ("cover_axis", (0.0, 0.0, PCB_BOTTOM_Z + PCB_THICKNESS / 2.0), "Rear-cover PCB plane"), ("phase_terminal", (*PHASE_TERMINAL_CENTER, PCB_BOTTOM_Z), "Phase terminal solder datum"), ("power_can_terminal", (*POWER_CAN_TERMINAL_CENTER, PCB_BOTTOM_Z), "Power/CAN terminal solder datum"), ("phase_access", (*PHASE_TERMINAL_CENTER, -37.0), "Phase terminal service axis"), ("power_can_access", (*POWER_CAN_TERMINAL_CENTER, -37.0), "Power/CAN service axis"), ] for index, angle in enumerate(MOSFET_ANGLES): radians = math.radians(angle) center = (13.2 * math.cos(radians), 13.2 * math.sin(radians)) connectors.append( ( f"mosfet_{index + 1}", (center[0], center[1], PCB_BOTTOM_Z + PCB_THICKNESS), f"MOSFET {index + 1} solder datum", ) ) print("pcb_holes: center=10.0 mount=4 column_notches=4 terminal_pins=7") return make_axis_part_rpart( part_id="circular_controller_pcb", body=board, name="44.4 mm circular ESC PCB with service cutouts", material=material, connectors=connectors, ) def _make_mosfet_package_rpart(*, material: scad.Material) -> scad.Part: package = scad.make_box_rsolid( width=4.0, height=3.0, depth=1.4, bottom_face_center=(0.0, 0.0, 0.0), ) package = apply_tags(shape=package, tags=("role.power_mosfet", "group.three_phase_bridge")) return make_axis_part_rpart( part_id="reusable_power_mosfet", body=package, name="Reusable power MOSFET package", material=material, connectors=(("solder_axis", (0.0, 0.0, 0.0), "PCB solder plane"),), ) def _make_terminal_block_rpart( *, part_id: str, name: str, width: float, pin_count: int, material: scad.Material, ) -> scad.Part: body = scad.make_box_rsolid( width=width, height=6.0, depth=4.9, bottom_face_center=(0.0, 0.0, 0.0), ) access_cutters = [] for pin in range(pin_count): y = (pin - (pin_count - 1) / 2.0) * 1.8 access_cutters.append( scad.make_cylinder_rsolid( radius=0.75, height=width + 2.0, bottom_face_center=(-width / 2.0 - 1.0, y, 2.45), axis=(1.0, 0.0, 0.0), ) ) body = scad.cut_rsolid(body, access_cutters, skip_non_intersecting=False) body = apply_tags(shape=body, tags=("role.rear_wiring_terminal", "role.service_access")) print(f"terminal_{part_id}: pins={pin_count} access_holes={pin_count} width={width:.1f}") return make_axis_part_rpart( part_id=part_id, body=body, name=name, material=material, connectors=(("solder_axis", (0.0, 0.0, 4.9), "PCB solder and screw datum"),), )