"""Top-level integrated BLDC motor, controller, reducer, and housing assembly.""" from __future__ import annotations import simplecadapi as scad try: from .bearings import ( make_coaxial_bearing_rplacement, make_main_bearing_rassembly, make_planet_bearing_rplacement, make_standard_planet_bearing_rassembly, ) from .common import connector_ref, ground_constraint_report from .dimensions import ( FRONT_MOTOR_BEARING, FRONT_MOTOR_BEARING_CENTER_Z, INTERSTAGE_BEARING, INTERSTAGE_BEARING_CENTER_Z, OUTPUT_BEARING, OUTPUT_BEARING_1_CENTER_Z, OUTPUT_BEARING_2_CENTER_Z, PLANET_BEARING, PLANET_COUNT, REAR_BEARING_CENTER_Z, REAR_MOTOR_BEARING, STAGE_1, STAGE_2, TOTAL_REDUCTION, StageSpec, ) from .electronics import make_integrated_controller_rassembly from .gears import ( make_output_carrier_flange_rpart, make_planet_rplacement, make_stage1_carrier_sun_rpart, make_stage_planet_gear_rpart, make_stage_ring_gear_rpart, ) from .housing import ( make_motor_shell_rpart, make_output_bearing_cap_rpart, make_rear_bearing_spider_rpart, make_rear_electronics_cover_rpart, make_reducer_housing_rpart, ) from .motor import make_bldc_rotor_rassembly, make_bldc_stator_rassembly except ImportError: # Support direct execution from this example directory. from bearings import ( make_coaxial_bearing_rplacement, make_main_bearing_rassembly, make_planet_bearing_rplacement, make_standard_planet_bearing_rassembly, ) from common import connector_ref, ground_constraint_report from dimensions import ( FRONT_MOTOR_BEARING, FRONT_MOTOR_BEARING_CENTER_Z, INTERSTAGE_BEARING, INTERSTAGE_BEARING_CENTER_Z, OUTPUT_BEARING, OUTPUT_BEARING_1_CENTER_Z, OUTPUT_BEARING_2_CENTER_Z, PLANET_BEARING, PLANET_COUNT, REAR_BEARING_CENTER_Z, REAR_MOTOR_BEARING, STAGE_1, STAGE_2, TOTAL_REDUCTION, StageSpec, ) from electronics import make_integrated_controller_rassembly from gears import ( make_output_carrier_flange_rpart, make_planet_rplacement, make_stage1_carrier_sun_rpart, make_stage_planet_gear_rpart, make_stage_ring_gear_rpart, ) from housing import ( make_motor_shell_rpart, make_output_bearing_cap_rpart, make_rear_bearing_spider_rpart, make_rear_electronics_cover_rpart, make_reducer_housing_rpart, ) from motor import make_bldc_rotor_rassembly, make_bldc_stator_rassembly @scad.requires_session def make_integrated_bldc_joint_actuator_rassembly( *, materials: dict[str, scad.Material] ) -> scad.Assembly: """Build and solve the complete compact 50 mm joint actuator.""" component_specs = make_integrated_bldc_joint_actuator_components_rtuple( materials=materials ) actuator = scad.make_assembly_rassembly( assembly_id="integrated_50mm_bldc_joint_actuator", name="50 mm 12-slot/14-pole BLDC joint actuator with 20:1 reducer and circular ESC", ) for component_id, item, placement, name in component_specs: actuator = scad.add_component_rassembly( assembly=actuator, item=item, component_id=component_id, placement=placement, name=name, ) actuator = _add_public_connectors_rassembly(assembly=actuator) actuator = _add_constraints_rassembly(assembly=actuator) actuator = scad.solve_assembly_constraints_rassembly(assembly=actuator, strict=True) ground_constraint_report(label="actuator", assembly=actuator) return actuator @scad.requires_session def make_integrated_bldc_joint_actuator_components_rtuple( *, materials: dict[str, scad.Material] ) -> tuple[tuple[str, scad.Part | scad.Assembly, scad.Placement, str], ...]: """Build the actuator component inventory without creating a parent assembly.""" print( f"ratio_plan: stage1={STAGE_1.fixed_ring_ratio:.1f}:1 " f"stage2={STAGE_2.fixed_ring_ratio:.1f}:1 total={TOTAL_REDUCTION:.1f}:1" ) reducer_housing = make_reducer_housing_rpart(material=materials["housing"]) motor_shell = make_motor_shell_rpart(material=materials["housing"]) rear_spider = make_rear_bearing_spider_rpart(material=materials["carrier"]) rear_cover = make_rear_electronics_cover_rpart(material=materials["housing"]) output_cap = make_output_bearing_cap_rpart(material=materials["carrier"]) stator = make_bldc_stator_rassembly( steel_material=materials["electrical_steel"], copper_material=materials["copper"], ) rotor = make_bldc_rotor_rassembly( steel_material=materials["gear"], magnet_material=materials["magnet"], ) controller = make_integrated_controller_rassembly( pcb_material=materials["pcb"], terminal_material=materials["terminal"], ) stage1_ring = make_stage_ring_gear_rpart(stage=STAGE_1, material=materials["gear"]) stage1_planet = make_stage_planet_gear_rpart(stage=STAGE_1, material=materials["gear"]) stage1_carrier = make_stage1_carrier_sun_rpart(material=materials["gear"]) stage2_ring = make_stage_ring_gear_rpart(stage=STAGE_2, material=materials["gear"]) stage2_planet = make_stage_planet_gear_rpart(stage=STAGE_2, material=materials["gear"]) output_carrier = make_output_carrier_flange_rpart(stage=STAGE_2, material=materials["carrier"]) rear_motor_bearing = make_main_bearing_rassembly( bearing_id="rear_motor_8x16x5", spec=REAR_MOTOR_BEARING, material=materials["gear"], ) front_motor_bearing = make_main_bearing_rassembly( bearing_id="front_motor_8x19x6", spec=FRONT_MOTOR_BEARING, material=materials["gear"], ) interstage_bearing = make_main_bearing_rassembly( bearing_id="interstage_5x10x3", spec=INTERSTAGE_BEARING, material=materials["gear"], ) planet_bearing = make_standard_planet_bearing_rassembly( bearing_id="planet_3x6x3", spec=PLANET_BEARING, material=materials["gear"], ) output_bearing = make_main_bearing_rassembly( bearing_id="output_16x24x5", spec=OUTPUT_BEARING, material=materials["gear"], ) fixed_components = ( ("reducer_housing", reducer_housing, scad.identity_placement_rplacement(), "Fixed reducer housing"), ("motor_shell", motor_shell, scad.identity_placement_rplacement(), "Fixed BLDC shell"), ("rear_bearing_spider", rear_spider, scad.identity_placement_rplacement(), "Rear motor-bearing spider"), ("rear_electronics_cover", rear_cover, scad.identity_placement_rplacement(), "Rear controller cover"), ("output_bearing_cap", output_cap, scad.identity_placement_rplacement(), "Output bearing cap"), ("stator", stator, scad.identity_placement_rplacement(), "12-slot fixed stator"), ("rotor", rotor, scad.identity_placement_rplacement(), "14-pole rotor and direct sun shaft"), ("controller", controller, scad.identity_placement_rplacement(), "Circular integrated controller"), ("stage1_carrier", stage1_carrier, scad.identity_placement_rplacement(), "Stage 1 carrier and stage 2 sun"), ("output_carrier", output_carrier, scad.identity_placement_rplacement(), "Stage 2 carrier and output flange"), ("stage1_ring", stage1_ring, _stage_rplacement(stage=STAGE_1), "Stage 1 fixed ring insert"), ("stage2_ring", stage2_ring, _stage_rplacement(stage=STAGE_2), "Stage 2 fixed ring insert"), ) print(f"actuator_base_components: count={len(fixed_components)}") planet_components = [] for stage, planet in ((STAGE_1, stage1_planet), (STAGE_2, stage2_planet)): for index in range(PLANET_COUNT): planet_components.append( ( f"{stage.stage_id}_planet_{index + 1}", planet, make_planet_rplacement(stage=stage, index=index), f"{stage.label} planet {index + 1}", ) ) bearing_components = ( ( "rear_motor_bearing", rear_motor_bearing, make_coaxial_bearing_rplacement(center_z=REAR_BEARING_CENTER_Z), "Rear rotor bearing", ), ( "front_motor_bearing", front_motor_bearing, make_coaxial_bearing_rplacement(center_z=FRONT_MOTOR_BEARING_CENTER_Z), "Front rotor bearing", ), ( "interstage_bearing", interstage_bearing, make_coaxial_bearing_rplacement(center_z=INTERSTAGE_BEARING_CENTER_Z), "Stage 1 carrier support bearing", ), ( "output_bearing_1", output_bearing, make_coaxial_bearing_rplacement(center_z=OUTPUT_BEARING_1_CENTER_Z), "Rear output bearing", ), ( "output_bearing_2", output_bearing, make_coaxial_bearing_rplacement(center_z=OUTPUT_BEARING_2_CENTER_Z), "Front output bearing", ), ) planet_bearing_components = [] for stage in (STAGE_1, STAGE_2): for index in range(PLANET_COUNT): planet_bearing_components.append( ( f"{stage.stage_id}_planet_bearing_{index + 1}", planet_bearing, make_planet_bearing_rplacement(stage=stage, index=index), f"{stage.label} planet bearing {index + 1}", ) ) print(f"bearing_components: motor=2 interstage=1 output=2 planet={PLANET_COUNT * 2}") return tuple( [ *fixed_components, *planet_components, *bearing_components, *planet_bearing_components, ] ) @scad.requires_session def _add_public_connectors_rassembly(*, assembly: scad.Assembly) -> scad.Assembly: forwarded = ( ("case_clamp_axis", "reducer_housing", "case_clamp_axis", "External split-clamp datum"), ("case_mount_axis", "output_bearing_cap", "case_mount_axis", "Fixed actuator case datum"), ("output_link_axis", "output_carrier", "output_link_axis", "Rotating six-hole output flange"), ("phase_terminal_access", "controller", "phase_access", "Rear phase-terminal service datum"), ("power_can_terminal_access", "controller", "power_can_access", "Rear power/CAN service datum"), ) for connector_id, source_component_id, source_connector_id, name in forwarded: assembly = scad.forward_connector_rassembly( assembly=assembly, connector_id=connector_id, source_component_id=source_component_id, source_connector_id=source_connector_id, name=name, offset=None, ) print("actuator_public_connectors: " + ",".join(item[0] for item in forwarded)) return assembly @scad.requires_session def _add_constraints_rassembly(*, assembly: scad.Assembly) -> scad.Assembly: assembly = scad.ground_component_rassembly(assembly=assembly, component_id="reducer_housing") assembly = scad.ground_component_rassembly(assembly=assembly, component_id="stage1_ring") assembly = scad.ground_component_rassembly(assembly=assembly, component_id="stage2_ring") fixed_pairs = ( ("motor_shell_to_reducer_housing", "reducer_housing", "motor_mount_axis", "motor_shell", "reducer_mount_axis"), ("rear_spider_to_motor_shell", "motor_shell", "rear_spider_axis", "rear_bearing_spider", "shell_axis"), ("rear_cover_to_motor_shell", "motor_shell", "rear_cover_axis", "rear_electronics_cover", "shell_axis"), ("stator_to_motor_shell", "motor_shell", "stator_axis", "stator", "shell_axis"), ("controller_to_rear_cover", "rear_electronics_cover", "pcb_axis", "controller", "cover_axis"), ("stage1_ring_fixed", "reducer_housing", "stage1_ring_axis", "stage1_ring", "axis"), ("stage2_ring_fixed", "reducer_housing", "stage2_ring_axis", "stage2_ring", "axis"), ("output_cap_to_reducer_housing", "reducer_housing", "output_cap_axis", "output_bearing_cap", "housing_axis"), ) for constraint_id, a_component, a_connector, b_component, b_connector in fixed_pairs: assembly = scad.add_fixed_constraint_rassembly( assembly=assembly, constraint_id=constraint_id, connector_a=connector_ref(component_id=a_component, connector_id=a_connector), connector_b=connector_ref(component_id=b_component, connector_id=b_connector), name=constraint_id.replace("_", " "), ) primary_revolutes = ( ("rotor_revolute", "reducer_housing", "front_motor_bearing_axis", "rotor", "front_bearing_axis"), ("stage1_carrier_revolute", "reducer_housing", "stage1_carrier_axis", "stage1_carrier", "carrier_axis"), ("output_carrier_revolute", "reducer_housing", "stage2_carrier_axis", "output_carrier", "carrier_axis"), ) for constraint_id, a_component, a_connector, b_component, b_connector in primary_revolutes: assembly = scad.add_revolute_constraint_rassembly( assembly=assembly, constraint_id=constraint_id, connector_a=connector_ref(component_id=a_component, connector_id=a_connector), connector_b=connector_ref(component_id=b_component, connector_id=b_connector), drive_angle_degrees=0.0, angle_limit=None, name=constraint_id.replace("_", " "), ) assembly = _add_stage_constraints_rassembly( assembly=assembly, stage=STAGE_1, sun_component="rotor", sun_connector="front_bearing_axis", ring_component="stage1_ring", carrier_component="stage1_carrier", ) assembly = _add_stage_constraints_rassembly( assembly=assembly, stage=STAGE_2, sun_component="stage1_carrier", sun_connector="carrier_axis", ring_component="stage2_ring", carrier_component="output_carrier", ) assembly = _add_bearing_constraints_rassembly(assembly=assembly) print("actuator_constraints: fixed=8 primary_revolute=3 planet_revolute=6 gear=6 internal=6 bearing_interfaces=22") return assembly @scad.requires_session def _add_stage_constraints_rassembly( *, assembly: scad.Assembly, stage: StageSpec, sun_component: str, sun_connector: str, ring_component: str, carrier_component: str, ) -> scad.Assembly: for index in range(PLANET_COUNT): planet_component = f"{stage.stage_id}_planet_{index + 1}" assembly = scad.add_revolute_constraint_rassembly( assembly=assembly, constraint_id=f"{planet_component}_revolute", connector_a=connector_ref(component_id=carrier_component, connector_id=f"planet_{index + 1}_axis"), connector_b=connector_ref(component_id=planet_component, connector_id="axis"), drive_angle_degrees=None, angle_limit=None, name=f"{stage.label} planet {index + 1} bearing axis", ) assembly = scad.add_gear_constraint_rassembly( assembly=assembly, constraint_id=f"{stage.stage_id}_sun_planet_{index + 1}_mesh", connector_a=connector_ref(component_id=sun_component, connector_id=sun_connector), connector_b=connector_ref(component_id=planet_component, connector_id="axis"), pitch_radius_a=stage.sun_pitch_radius, pitch_radius_b=stage.planet_pitch_radius, phase_offset=None, name=f"{stage.label} sun to planet {index + 1} external mesh", ) assembly = scad.add_belt_constraint_rassembly( assembly=assembly, constraint_id=f"{stage.stage_id}_ring_planet_{index + 1}_internal_mesh", connector_a=connector_ref(component_id=ring_component, connector_id="axis"), connector_b=connector_ref(component_id=planet_component, connector_id="axis"), pulley_radius_a=stage.ring_pitch_radius, pulley_radius_b=stage.planet_pitch_radius, phase_offset=None, name=f"{stage.label} fixed-ring to planet {index + 1} internal mesh", ) print( f"{stage.stage_id}_mesh: sun_r={stage.sun_pitch_radius:.3f} " f"planet_r={stage.planet_pitch_radius:.3f} center={stage.planet_center_radius:.3f}" ) return assembly @scad.requires_session def _add_bearing_constraints_rassembly(*, assembly: scad.Assembly) -> scad.Assembly: interfaces = ( ("rear_bearing_outer_to_spider", "rear_bearing_spider", "bearing_axis", "rear_motor_bearing", "outer_axis"), ("rear_bearing_inner_to_rotor", "rotor", "rear_bearing_axis", "rear_motor_bearing", "inner_axis"), ("front_bearing_outer_to_housing", "reducer_housing", "front_motor_bearing_axis", "front_motor_bearing", "outer_axis"), ("front_bearing_inner_to_rotor", "rotor", "front_bearing_axis", "front_motor_bearing", "inner_axis"), ("interstage_bearing_outer_to_housing", "reducer_housing", "interstage_bearing_axis", "interstage_bearing", "outer_axis"), ("interstage_bearing_inner_to_carrier", "stage1_carrier", "interstage_bearing_axis", "interstage_bearing", "inner_axis"), ("output_bearing_1_outer_to_cap", "output_bearing_cap", "bearing_1_axis", "output_bearing_1", "outer_axis"), ("output_bearing_1_inner_to_carrier", "output_carrier", "bearing_1_axis", "output_bearing_1", "inner_axis"), ("output_bearing_2_outer_to_cap", "output_bearing_cap", "bearing_2_axis", "output_bearing_2", "outer_axis"), ("output_bearing_2_inner_to_carrier", "output_carrier", "bearing_2_axis", "output_bearing_2", "inner_axis"), ) for constraint_id, a_component, a_connector, b_component, b_connector in interfaces: assembly = scad.add_revolute_constraint_rassembly( assembly=assembly, constraint_id=constraint_id, connector_a=connector_ref(component_id=a_component, connector_id=a_connector), connector_b=connector_ref(component_id=b_component, connector_id=b_connector), drive_angle_degrees=None, angle_limit=None, name=constraint_id.replace("_", " "), ) for stage, carrier_component in ((STAGE_1, "stage1_carrier"), (STAGE_2, "output_carrier")): for index in range(PLANET_COUNT): planet = f"{stage.stage_id}_planet_{index + 1}" bearing = f"{stage.stage_id}_planet_bearing_{index + 1}" assembly = scad.add_revolute_constraint_rassembly( assembly=assembly, constraint_id=f"{bearing}_outer_to_planet", connector_a=connector_ref(component_id=planet, connector_id="bearing_axis"), connector_b=connector_ref(component_id=bearing, connector_id="outer_axis"), drive_angle_degrees=None, angle_limit=None, name=f"{stage.label} planet {index + 1} bearing outer-ring fit", ) assembly = scad.add_revolute_constraint_rassembly( assembly=assembly, constraint_id=f"{bearing}_inner_to_pin", connector_a=connector_ref( component_id=carrier_component, connector_id=f"planet_{index + 1}_bearing_axis", ), connector_b=connector_ref(component_id=bearing, connector_id="inner_axis"), drive_angle_degrees=None, angle_limit=None, name=f"{stage.label} planet {index + 1} bearing inner-ring pin fit", ) return assembly @scad.requires_session def _stage_rplacement(*, stage: StageSpec) -> scad.Placement: return scad.make_placement_rplacement(origin=(0.0, 0.0, stage.bottom_z))