first commit
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
"""Assembly and kinematic constraints for a four-planet planetary reducer."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import simplecadapi as scad
|
||||
|
||||
from dimensions import (
|
||||
FIXED_RING_REDUCTION,
|
||||
PLANET_COUNT,
|
||||
PLANET_PITCH_RADIUS,
|
||||
RING_PITCH_RADIUS,
|
||||
SUN_PITCH_RADIUS,
|
||||
)
|
||||
from materials import make_materials_rdict
|
||||
from parts import (
|
||||
make_carrier_rpart,
|
||||
make_planet_component_rplacement,
|
||||
make_planet_gear_rpart,
|
||||
make_ring_gear_rpart,
|
||||
make_sun_gear_rpart,
|
||||
)
|
||||
|
||||
|
||||
def make_four_planet_planetary_reducer_rassembly() -> scad.Assembly:
|
||||
"""Build and solve the exposed four-planet fixed-ring reducer gearset."""
|
||||
|
||||
print(
|
||||
f"ratio_plan: fixed_ring={FIXED_RING_REDUCTION:.3f}:1 "
|
||||
f"planets={PLANET_COUNT} sun_r={SUN_PITCH_RADIUS:.3f} "
|
||||
f"planet_r={PLANET_PITCH_RADIUS:.3f} ring_r={RING_PITCH_RADIUS:.3f}"
|
||||
)
|
||||
materials = make_materials_rdict()
|
||||
sun = make_sun_gear_rpart(material=materials["gear"])
|
||||
ring = make_ring_gear_rpart(material=materials["ring"])
|
||||
planet = make_planet_gear_rpart(material=materials["gear"])
|
||||
carrier = make_carrier_rpart(material=materials["carrier"])
|
||||
|
||||
reducer = scad.make_assembly_rassembly(
|
||||
assembly_id="four_planet_planetary_reducer",
|
||||
name="Exposed 3.5:1 four-planet fixed-ring planetary reducer gearset",
|
||||
)
|
||||
for component_id, item, placement, name in (
|
||||
("fixed_ring", ring, scad.identity_placement_rplacement(), "Fixed internal ring gear"),
|
||||
("sun_input", sun, scad.identity_placement_rplacement(), "Input sun gear"),
|
||||
("output_carrier", carrier, scad.identity_placement_rplacement(), "Four-pin output carrier"),
|
||||
):
|
||||
reducer = scad.add_component_rassembly(
|
||||
assembly=reducer,
|
||||
item=item,
|
||||
component_id=component_id,
|
||||
placement=placement,
|
||||
name=name,
|
||||
)
|
||||
for index in range(PLANET_COUNT):
|
||||
reducer = scad.add_component_rassembly(
|
||||
assembly=reducer,
|
||||
item=planet,
|
||||
component_id=f"planet_{index + 1}",
|
||||
placement=make_planet_component_rplacement(index=index),
|
||||
name=f"Planet gear {index + 1}",
|
||||
)
|
||||
|
||||
reducer = _add_public_connectors_rassembly(assembly=reducer)
|
||||
reducer = _add_kinematic_constraints_rassembly(assembly=reducer)
|
||||
reducer = scad.solve_assembly_constraints_rassembly(assembly=reducer, strict=True)
|
||||
_ground_constraint_report(assembly=reducer)
|
||||
print(
|
||||
f"planetary_components: count={len(reducer.component_ids())} "
|
||||
f"constraints={len(reducer.constraint_ids())}"
|
||||
)
|
||||
return reducer
|
||||
|
||||
|
||||
def _add_public_connectors_rassembly(*, assembly: scad.Assembly) -> scad.Assembly:
|
||||
forwarded = (
|
||||
("fixed_axis", "fixed_ring", "axis", "Fixed ring datum"),
|
||||
("input_axis", "sun_input", "axis", "Sun input datum"),
|
||||
("output_axis", "output_carrier", "output_axis", "Carrier output datum"),
|
||||
)
|
||||
for connector_id, component_id, source_connector_id, name in forwarded:
|
||||
assembly = scad.forward_connector_rassembly(
|
||||
assembly=assembly,
|
||||
connector_id=connector_id,
|
||||
source_component_id=component_id,
|
||||
source_connector_id=source_connector_id,
|
||||
name=name,
|
||||
)
|
||||
print("public_connectors: " + ",".join(connector_id for connector_id, *_ in forwarded))
|
||||
return assembly
|
||||
|
||||
|
||||
def _add_kinematic_constraints_rassembly(*, assembly: scad.Assembly) -> scad.Assembly:
|
||||
assembly = scad.ground_component_rassembly(assembly=assembly, component_id="fixed_ring")
|
||||
revolutes = (
|
||||
("sun_input_revolute", "fixed_ring", "axis", "sun_input", "axis", 0.0),
|
||||
("carrier_output_revolute", "fixed_ring", "axis", "output_carrier", "axis", 0.0),
|
||||
)
|
||||
for constraint_id, a_component, a_connector, b_component, b_connector, drive_angle in revolutes:
|
||||
assembly = scad.add_revolute_constraint_rassembly(
|
||||
assembly=assembly,
|
||||
constraint_id=constraint_id,
|
||||
connector_a=_ref(component_id=a_component, connector_id=a_connector),
|
||||
connector_b=_ref(component_id=b_component, connector_id=b_connector),
|
||||
drive_angle_degrees=drive_angle,
|
||||
angle_limit=None,
|
||||
name=constraint_id.replace("_", " "),
|
||||
)
|
||||
for index in range(PLANET_COUNT):
|
||||
planet_id = f"planet_{index + 1}"
|
||||
assembly = scad.add_revolute_constraint_rassembly(
|
||||
assembly=assembly,
|
||||
constraint_id=f"planet_{index + 1}_pin_revolute",
|
||||
connector_a=_ref(component_id="output_carrier", connector_id=f"planet_{index + 1}_axis"),
|
||||
connector_b=_ref(component_id=planet_id, connector_id="axis"),
|
||||
drive_angle_degrees=None,
|
||||
angle_limit=None,
|
||||
name=f"Planet {index + 1} pin bearing revolute",
|
||||
)
|
||||
assembly = scad.add_gear_constraint_rassembly(
|
||||
assembly=assembly,
|
||||
constraint_id=f"sun_to_planet_{index + 1}_external_mesh",
|
||||
connector_a=_ref(component_id="sun_input", connector_id="axis"),
|
||||
connector_b=_ref(component_id=planet_id, connector_id="axis"),
|
||||
pitch_radius_a=SUN_PITCH_RADIUS,
|
||||
pitch_radius_b=PLANET_PITCH_RADIUS,
|
||||
phase_offset=None,
|
||||
name=f"Sun external mesh to planet {index + 1}",
|
||||
)
|
||||
assembly = scad.add_belt_constraint_rassembly(
|
||||
assembly=assembly,
|
||||
constraint_id=f"ring_to_planet_{index + 1}_internal_mesh",
|
||||
connector_a=_ref(component_id="fixed_ring", connector_id="axis"),
|
||||
connector_b=_ref(component_id=planet_id, connector_id="axis"),
|
||||
pulley_radius_a=RING_PITCH_RADIUS,
|
||||
pulley_radius_b=PLANET_PITCH_RADIUS,
|
||||
phase_offset=None,
|
||||
name=f"Fixed ring internal mesh to planet {index + 1}",
|
||||
)
|
||||
print(
|
||||
f"constraints_added: grounded=1 revolute={2 + PLANET_COUNT} "
|
||||
f"external_mesh={PLANET_COUNT} internal_mesh={PLANET_COUNT}"
|
||||
)
|
||||
return assembly
|
||||
|
||||
|
||||
def _ref(*, component_id: str, connector_id: str) -> scad.ConnectorRef:
|
||||
return scad.make_connector_ref_rconnectorref(
|
||||
component_id=component_id,
|
||||
connector_id=connector_id,
|
||||
)
|
||||
|
||||
|
||||
def _ground_constraint_report(*, assembly: scad.Assembly) -> None:
|
||||
report = scad.inspect_assembly_constraints_rconstraintreport(assembly=assembly)
|
||||
print(
|
||||
f"assembly_constraints: solved={report.solved} grounded={len(report.grounded_component_ids)} "
|
||||
f"solved_components={len(report.solved_component_ids)} unsolved={len(report.unsolved_component_ids)}"
|
||||
)
|
||||
for residual in report.residuals:
|
||||
print(
|
||||
f"constraint_{residual.constraint_id}: translation={residual.translation_error:.6g} "
|
||||
f"angle={residual.angular_error_degrees:.6g} ok={residual.within_tolerance}"
|
||||
)
|
||||
@@ -0,0 +1,78 @@
|
||||
"""Shared helpers for Example 19."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
|
||||
import simplecadapi as scad
|
||||
from simplecadapi import ql
|
||||
|
||||
|
||||
def make_z_rotation_rplacement(
|
||||
*,
|
||||
origin: tuple[float, float, float],
|
||||
angle_degrees: float,
|
||||
) -> scad.Placement:
|
||||
"""Create a placement rotated about local Z and translated to origin."""
|
||||
|
||||
angle = math.radians(angle_degrees)
|
||||
return scad.make_placement_rplacement(
|
||||
origin=origin,
|
||||
x_axis=(math.cos(angle), math.sin(angle), 0.0),
|
||||
y_axis=(-math.sin(angle), math.cos(angle), 0.0),
|
||||
)
|
||||
|
||||
|
||||
def add_axis_connector_rpart(
|
||||
*,
|
||||
part: scad.Part,
|
||||
connector_id: str,
|
||||
origin: tuple[float, float, float],
|
||||
name: str,
|
||||
) -> scad.Part:
|
||||
"""Attach a topology-free Z-axis datum connector to a part."""
|
||||
|
||||
connector = scad.make_placement_connector_rconnector(
|
||||
connector_id=connector_id,
|
||||
placement=scad.make_placement_rplacement(
|
||||
origin=origin,
|
||||
x_axis=(1.0, 0.0, 0.0),
|
||||
y_axis=(0.0, 1.0, 0.0),
|
||||
),
|
||||
name=name,
|
||||
)
|
||||
return scad.add_connector_rpart(part=part, connector=connector)
|
||||
|
||||
|
||||
def make_axis_part_rpart(
|
||||
*,
|
||||
part_id: str,
|
||||
body: scad.Solid,
|
||||
name: str,
|
||||
material: scad.Material,
|
||||
connector_specs: tuple[tuple[str, tuple[float, float, float], str], ...],
|
||||
) -> scad.Part:
|
||||
"""Wrap one solid as a part and attach named axis connectors."""
|
||||
|
||||
part = scad.make_part_rpart(part_id=part_id, body=body, name=name)
|
||||
part = scad.assign_material_rpart(part=part, material=material)
|
||||
for connector_id, origin, connector_name in connector_specs:
|
||||
part = add_axis_connector_rpart(
|
||||
part=part,
|
||||
connector_id=connector_id,
|
||||
origin=origin,
|
||||
name=connector_name,
|
||||
)
|
||||
print(f"part_{part_id}: connectors={len(part.connectors)} volume={body.get_volume():.3f}")
|
||||
return part
|
||||
|
||||
|
||||
def ground_solid(*, label: str, solid: scad.Solid) -> None:
|
||||
"""Print a small QL-grounded summary for a generated solid."""
|
||||
|
||||
faces = ql.select(items=solid.get_faces()).all()
|
||||
edges = ql.select(items=solid.get_edges()).all()
|
||||
print(
|
||||
f"{label}: faces={len(faces)} edges={len(edges)} "
|
||||
f"volume={solid.get_volume():.3f} tags={','.join(scad.list_tags(shape=solid))}"
|
||||
)
|
||||
@@ -0,0 +1,54 @@
|
||||
"""Design constants for the four-planet single-stage planetary reducer."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
|
||||
|
||||
MODULE = 1.5
|
||||
PRESSURE_ANGLE = 20.0
|
||||
GEAR_HEIGHT = 8.0
|
||||
BACKLASH = 0.04
|
||||
ADDENDUM_FACTOR = 1.0
|
||||
CLEARANCE_FACTOR = 0.25
|
||||
|
||||
SUN_TEETH = 24
|
||||
PLANET_TEETH = 18
|
||||
PLANET_COUNT = 4
|
||||
RING_TEETH = SUN_TEETH + 2 * PLANET_TEETH
|
||||
|
||||
SUN_PITCH_RADIUS = MODULE * SUN_TEETH / 2.0
|
||||
PLANET_PITCH_RADIUS = MODULE * PLANET_TEETH / 2.0
|
||||
RING_PITCH_RADIUS = MODULE * RING_TEETH / 2.0
|
||||
PLANET_CENTER_RADIUS = SUN_PITCH_RADIUS + PLANET_PITCH_RADIUS
|
||||
FIXED_RING_REDUCTION = 1.0 + RING_TEETH / SUN_TEETH
|
||||
|
||||
RING_RIM_THICKNESS = 4.0
|
||||
GEAR_AXIS_Z = GEAR_HEIGHT / 2.0
|
||||
|
||||
SUN_BORE_RADIUS = 3.0
|
||||
PLANET_PIN_RADIUS = 2.6
|
||||
PLANET_PIN_CLEARANCE_RADIUS = 3.2
|
||||
|
||||
CARRIER_BOTTOM_Z = -5.0
|
||||
CARRIER_THICKNESS = 4.0
|
||||
CARRIER_HUB_RADIUS = 10.0
|
||||
CARRIER_ARM_WIDTH = 6.0
|
||||
CARRIER_PIN_BOSS_RADIUS = 5.2
|
||||
CARRIER_PIN_HEIGHT = GEAR_HEIGHT + 6.0
|
||||
|
||||
|
||||
def planet_angle_degrees(*, index: int) -> float:
|
||||
"""Return the equally spaced carrier angle for one planet index."""
|
||||
|
||||
return 360.0 * index / PLANET_COUNT
|
||||
|
||||
|
||||
def planet_center_xy(*, index: int) -> tuple[float, float]:
|
||||
"""Return the XY pitch-center location for one planet."""
|
||||
|
||||
angle = math.radians(planet_angle_degrees(index=index))
|
||||
return (
|
||||
PLANET_CENTER_RADIUS * math.cos(angle),
|
||||
PLANET_CENTER_RADIUS * math.sin(angle),
|
||||
)
|
||||
@@ -0,0 +1,71 @@
|
||||
"""Build, solve, and export the four-planet planetary reducer gearset."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import simplecadapi as scad
|
||||
|
||||
from assembly import make_four_planet_planetary_reducer_rassembly
|
||||
|
||||
|
||||
OUT_DIR = Path("examples/out/19_four_planet_planetary_reducer")
|
||||
|
||||
|
||||
def _build_four_planet_reducer():
|
||||
with scad.GraphSession(graph_id="four_planet_planetary_reducer") as session:
|
||||
assembly = make_four_planet_planetary_reducer_rassembly()
|
||||
preview = scad.make_compound_from_assembly_rcompound(assembly=assembly)
|
||||
session_json = scad.export_session_json(session=session)
|
||||
model_json = scad.export_model_json(session=session)
|
||||
return assembly, preview, model_json, session_json
|
||||
|
||||
|
||||
def main() -> None:
|
||||
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
model_path = OUT_DIR / "four_planet_planetary_reducer.model.json"
|
||||
session_path = OUT_DIR / "four_planet_planetary_reducer.session.json"
|
||||
step_path = OUT_DIR / "four_planet_planetary_reducer.step"
|
||||
fcstd_path = OUT_DIR / "four_planet_planetary_reducer.FCStd"
|
||||
if fcstd_path.exists():
|
||||
fcstd_path.unlink()
|
||||
|
||||
assembly, preview, model_json, session_json = _build_four_planet_reducer()
|
||||
model_path.write_text(model_json, encoding="utf-8")
|
||||
session_path.write_text(session_json, encoding="utf-8")
|
||||
scad.export_step(shapes=preview, filename=str(step_path))
|
||||
|
||||
imported = scad.import_model_json(json_str=model_json)
|
||||
replayed = scad.replay_model_json(json_str=model_json)
|
||||
payload = json.loads(model_json)
|
||||
|
||||
fcstd_status = "not attempted"
|
||||
try:
|
||||
scad.translator.freecad_translator.translate_model_json_to_fcstd(
|
||||
json_str=model_json,
|
||||
output_path=str(fcstd_path.resolve()),
|
||||
document_name="FourPlanetPlanetaryReducer",
|
||||
freecad_cmd=None,
|
||||
)
|
||||
fcstd_status = f"{fcstd_path} ({fcstd_path.stat().st_size} bytes)"
|
||||
except Exception as exc: # pragma: no cover - depends on local FreeCAD install
|
||||
fcstd_status = f"skipped ({exc.__class__.__name__}: {exc})"
|
||||
|
||||
print(f"assembly={assembly.assembly_id}")
|
||||
print("components=" + ",".join(assembly.component_ids()))
|
||||
print("constraints=" + ",".join(assembly.constraint_ids()))
|
||||
print(f"preview_solids={len(preview.get_solids())}")
|
||||
print(f"preview_volume={preview.get_volume():.3f}")
|
||||
print(f"imported_keys={','.join(sorted(imported.keys()))}")
|
||||
print(f"replay_outputs={len(replayed)}")
|
||||
print("replay_types=" + ",".join(type(item).__name__ for item in replayed))
|
||||
print(f"graph_nodes={len(payload['graph']['nodes'])}")
|
||||
print(f"model={model_path}")
|
||||
print(f"session={session_path}")
|
||||
print(f"step={step_path}")
|
||||
print(f"fcstd={fcstd_status}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Materials for the four-planet planetary reducer example."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import simplecadapi as scad
|
||||
|
||||
|
||||
def make_materials_rdict() -> dict[str, scad.Material]:
|
||||
"""Create simple material definitions for the exposed gearset."""
|
||||
|
||||
materials = {
|
||||
"gear": scad.make_material_rmaterial(
|
||||
material_id="case_hardened_steel",
|
||||
name="Case hardened gear steel",
|
||||
density=7.85e-6,
|
||||
density_unit="kg/mm^3",
|
||||
color=(0.70, 0.70, 0.74),
|
||||
),
|
||||
"ring": scad.make_material_rmaterial(
|
||||
material_id="nitrided_internal_ring_steel",
|
||||
name="Nitrided internal ring steel",
|
||||
density=7.85e-6,
|
||||
density_unit="kg/mm^3",
|
||||
color=(0.44, 0.46, 0.50),
|
||||
),
|
||||
"carrier": scad.make_material_rmaterial(
|
||||
material_id="aluminum_7075_t6",
|
||||
name="7075-T6 aluminum carrier",
|
||||
density=2.81e-6,
|
||||
density_unit="kg/mm^3",
|
||||
color=(0.14, 0.48, 0.70),
|
||||
),
|
||||
}
|
||||
print("materials: " + ",".join(sorted(materials)))
|
||||
return materials
|
||||
@@ -0,0 +1,206 @@
|
||||
"""Gear and carrier parts for the four-planet planetary reducer."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import simplecadapi as scad
|
||||
|
||||
from common import ground_solid, make_axis_part_rpart, make_z_rotation_rplacement
|
||||
from dimensions import (
|
||||
ADDENDUM_FACTOR,
|
||||
BACKLASH,
|
||||
CARRIER_ARM_WIDTH,
|
||||
CARRIER_BOTTOM_Z,
|
||||
CARRIER_HUB_RADIUS,
|
||||
CARRIER_PIN_BOSS_RADIUS,
|
||||
CARRIER_PIN_HEIGHT,
|
||||
CARRIER_THICKNESS,
|
||||
CLEARANCE_FACTOR,
|
||||
GEAR_AXIS_Z,
|
||||
GEAR_HEIGHT,
|
||||
MODULE,
|
||||
PLANET_CENTER_RADIUS,
|
||||
PLANET_COUNT,
|
||||
PLANET_PIN_CLEARANCE_RADIUS,
|
||||
PLANET_PIN_RADIUS,
|
||||
PLANET_TEETH,
|
||||
PRESSURE_ANGLE,
|
||||
RING_RIM_THICKNESS,
|
||||
RING_TEETH,
|
||||
SUN_BORE_RADIUS,
|
||||
SUN_TEETH,
|
||||
planet_angle_degrees,
|
||||
planet_center_xy,
|
||||
)
|
||||
|
||||
|
||||
def make_sun_gear_rpart(*, material: scad.Material) -> scad.Part:
|
||||
"""Create the input sun gear with a service bore and axis connector."""
|
||||
|
||||
sun = scad.std.gear.make_spur_gear_rsolid(
|
||||
n_teeth=SUN_TEETH,
|
||||
module=MODULE,
|
||||
pressure_angle=PRESSURE_ANGLE,
|
||||
gear_height=GEAR_HEIGHT,
|
||||
addendum_factor=ADDENDUM_FACTOR,
|
||||
clearance_factor=CLEARANCE_FACTOR,
|
||||
backlash=BACKLASH,
|
||||
)
|
||||
bore = scad.make_cylinder_rsolid(
|
||||
radius=SUN_BORE_RADIUS,
|
||||
height=GEAR_HEIGHT + 2.0,
|
||||
bottom_face_center=(0.0, 0.0, -1.0),
|
||||
axis=(0.0, 0.0, 1.0),
|
||||
)
|
||||
sun = scad.cut_rsolid(sun, bore, skip_non_intersecting=False)
|
||||
sun = scad.apply_tag(shape=sun, tag="role.sun_input_gear")
|
||||
ground_solid(label="sun_gear", solid=sun)
|
||||
return make_axis_part_rpart(
|
||||
part_id="sun_input_gear",
|
||||
body=sun,
|
||||
name="Input sun gear, 24 teeth",
|
||||
material=material,
|
||||
connector_specs=(("axis", (0.0, 0.0, GEAR_AXIS_Z), "Sun input axis"),),
|
||||
)
|
||||
|
||||
|
||||
def make_ring_gear_rpart(*, material: scad.Material) -> scad.Part:
|
||||
"""Create the fixed internal ring gear without an enclosing housing."""
|
||||
|
||||
ring = scad.std.gear.make_spur_ring_gear_rsolid(
|
||||
n_teeth=RING_TEETH,
|
||||
module=MODULE,
|
||||
pressure_angle=PRESSURE_ANGLE,
|
||||
gear_height=GEAR_HEIGHT,
|
||||
rim_thickness=RING_RIM_THICKNESS,
|
||||
backlash=BACKLASH,
|
||||
addendum_factor=ADDENDUM_FACTOR,
|
||||
clearance_factor=CLEARANCE_FACTOR,
|
||||
)
|
||||
ring = scad.apply_tag(shape=ring, tag="role.fixed_internal_ring_gear")
|
||||
ground_solid(label="ring_gear", solid=ring)
|
||||
return make_axis_part_rpart(
|
||||
part_id="fixed_ring_gear",
|
||||
body=ring,
|
||||
name="Fixed internal ring gear, 60 teeth",
|
||||
material=material,
|
||||
connector_specs=(("axis", (0.0, 0.0, GEAR_AXIS_Z), "Fixed ring axis"),),
|
||||
)
|
||||
|
||||
|
||||
def make_planet_gear_rpart(*, material: scad.Material) -> scad.Part:
|
||||
"""Create one reusable planet gear with a carrier-pin bore."""
|
||||
|
||||
planet = scad.std.gear.make_spur_gear_rsolid(
|
||||
n_teeth=PLANET_TEETH,
|
||||
module=MODULE,
|
||||
pressure_angle=PRESSURE_ANGLE,
|
||||
gear_height=GEAR_HEIGHT,
|
||||
addendum_factor=ADDENDUM_FACTOR,
|
||||
clearance_factor=CLEARANCE_FACTOR,
|
||||
backlash=BACKLASH,
|
||||
)
|
||||
bore = scad.make_cylinder_rsolid(
|
||||
radius=PLANET_PIN_CLEARANCE_RADIUS,
|
||||
height=GEAR_HEIGHT + 2.0,
|
||||
bottom_face_center=(0.0, 0.0, -1.0),
|
||||
axis=(0.0, 0.0, 1.0),
|
||||
)
|
||||
planet = scad.cut_rsolid(planet, bore, skip_non_intersecting=False)
|
||||
planet = scad.apply_tag(shape=planet, tag="role.reusable_planet_gear")
|
||||
ground_solid(label="planet_gear", solid=planet)
|
||||
return make_axis_part_rpart(
|
||||
part_id="planet_gear",
|
||||
body=planet,
|
||||
name="Reusable planet gear, 18 teeth",
|
||||
material=material,
|
||||
connector_specs=(("axis", (0.0, 0.0, GEAR_AXIS_Z), "Planet spin axis"),),
|
||||
)
|
||||
|
||||
|
||||
def make_carrier_rpart(*, material: scad.Material) -> scad.Part:
|
||||
"""Create the four-pin carrier output spider."""
|
||||
|
||||
hub = scad.make_cylinder_rsolid(
|
||||
radius=CARRIER_HUB_RADIUS,
|
||||
height=CARRIER_THICKNESS,
|
||||
bottom_face_center=(0.0, 0.0, CARRIER_BOTTOM_Z),
|
||||
axis=(0.0, 0.0, 1.0),
|
||||
)
|
||||
arms: list[scad.Solid] = []
|
||||
pin_bosses: list[scad.Solid] = []
|
||||
pins: list[scad.Solid] = []
|
||||
for index in range(PLANET_COUNT):
|
||||
angle = planet_angle_degrees(index=index)
|
||||
x, y = planet_center_xy(index=index)
|
||||
arm = scad.make_box_rsolid(
|
||||
width=PLANET_CENTER_RADIUS + CARRIER_PIN_BOSS_RADIUS,
|
||||
height=CARRIER_ARM_WIDTH,
|
||||
depth=CARRIER_THICKNESS,
|
||||
bottom_face_center=(PLANET_CENTER_RADIUS / 2.0, 0.0, CARRIER_BOTTOM_Z),
|
||||
)
|
||||
arms.append(
|
||||
scad.rotate_shape(
|
||||
shape=arm,
|
||||
angle=angle,
|
||||
axis=(0.0, 0.0, 1.0),
|
||||
origin=(0.0, 0.0, 0.0),
|
||||
)
|
||||
)
|
||||
pin_bosses.append(
|
||||
scad.make_cylinder_rsolid(
|
||||
radius=CARRIER_PIN_BOSS_RADIUS,
|
||||
height=CARRIER_THICKNESS,
|
||||
bottom_face_center=(x, y, CARRIER_BOTTOM_Z),
|
||||
axis=(0.0, 0.0, 1.0),
|
||||
)
|
||||
)
|
||||
pins.append(
|
||||
scad.make_cylinder_rsolid(
|
||||
radius=PLANET_PIN_RADIUS,
|
||||
height=CARRIER_PIN_HEIGHT,
|
||||
bottom_face_center=(x, y, CARRIER_BOTTOM_Z),
|
||||
axis=(0.0, 0.0, 1.0),
|
||||
)
|
||||
)
|
||||
carrier = scad.union_rsolid([hub, arms, pin_bosses, pins], glue=False)
|
||||
center_bore = scad.make_cylinder_rsolid(
|
||||
radius=SUN_BORE_RADIUS + 0.8,
|
||||
height=CARRIER_THICKNESS + 2.0,
|
||||
bottom_face_center=(0.0, 0.0, CARRIER_BOTTOM_Z - 1.0),
|
||||
axis=(0.0, 0.0, 1.0),
|
||||
)
|
||||
carrier = scad.cut_rsolid(carrier, center_bore, skip_non_intersecting=False)
|
||||
carrier = scad.apply_tag(shape=carrier, tag="role.four_pin_output_carrier")
|
||||
ground_solid(label="carrier", solid=carrier)
|
||||
connector_specs = [
|
||||
("axis", (0.0, 0.0, GEAR_AXIS_Z), "Carrier output axis"),
|
||||
("output_axis", (0.0, 0.0, GEAR_AXIS_Z), "Public output axis"),
|
||||
]
|
||||
connector_specs.extend(
|
||||
(
|
||||
f"planet_{index + 1}_axis",
|
||||
(*planet_center_xy(index=index), GEAR_AXIS_Z),
|
||||
f"Planet {index + 1} carrier pin axis",
|
||||
)
|
||||
for index in range(PLANET_COUNT)
|
||||
)
|
||||
return make_axis_part_rpart(
|
||||
part_id="four_pin_output_carrier",
|
||||
body=carrier,
|
||||
name="Four-pin output carrier spider",
|
||||
material=material,
|
||||
connector_specs=tuple(connector_specs),
|
||||
)
|
||||
|
||||
|
||||
def make_planet_component_rplacement(*, index: int) -> scad.Placement:
|
||||
"""Return the placement for one of the four equally spaced planets."""
|
||||
|
||||
angle = planet_angle_degrees(index=index)
|
||||
x, y = planet_center_xy(index=index)
|
||||
tooth_phase = angle + 180.0 - (180.0 / PLANET_TEETH)
|
||||
print(
|
||||
f"planet_{index + 1}: center=({x:.3f},{y:.3f},0.000) "
|
||||
f"carrier_angle={angle:.1f} spin_phase={tooth_phase:.1f}"
|
||||
)
|
||||
return make_z_rotation_rplacement(origin=(x, y, 0.0), angle_degrees=tooth_phase)
|
||||
Reference in New Issue
Block a user