From 878d532890eb4894b7257254f10ee8e09a395a6f Mon Sep 17 00:00:00 2001 From: Leni Aniva Date: Fri, 9 May 2025 16:58:14 -0400 Subject: [PATCH] Use rotor-stator configuration for bearing --- nhf/touhou/yasaka_kanako/onbashira.py | 165 ++++++++++++++------------ 1 file changed, 86 insertions(+), 79 deletions(-) diff --git a/nhf/touhou/yasaka_kanako/onbashira.py b/nhf/touhou/yasaka_kanako/onbashira.py index 73bf69f..5750abb 100644 --- a/nhf/touhou/yasaka_kanako/onbashira.py +++ b/nhf/touhou/yasaka_kanako/onbashira.py @@ -17,15 +17,20 @@ class Onbashira(Model): side_thickness: float = 25.4 / 8 # Dimensions of gun barrels - barrel_diam: float = 45.0 + barrel_diam: float = 25.4 * 2 barrel_length: float = 300.0 # Radius from barrel centre to axis rotation_radius: float = 75.0 - # Radius of ball bearings - bearing_ball_diam: float = 30.0 - bearing_ball_gap: float = 1.0 + n_bearing_balls: int = 24 + # Size of ball bearings + bearing_ball_diam: float = 25.4 * 1/2 + bearing_ball_gap: float = .5 bearing_height: float = 40.0 bearing_thickness: float = 20.0 + bearing_track_radius: float = 120.0 + # Gap between the inner and outer bearing disks + bearing_gap: float = 10.0 + bearing_disk_thickness: float = 25.4 / 8 material_side: Material = Material.WOOD_BIRCH material_bearing: Material = Material.PLASTIC_PLA @@ -37,6 +42,7 @@ class Onbashira(Model): # Bulk must be large enough for the barrel + bearing to rotate assert self.bulk_radius - self.side_thickness - self.bearing_thickness - self.bearing_diam > self.rotation_radius + self.barrel_diam / 2 assert self.bearing_height > self.bearing_diam + assert self.bearing_gap < 0.95 * self.bearing_ball_diam @property def angle_side(self) -> float: @@ -53,9 +59,49 @@ class Onbashira(Model): @property def bearing_diam(self) -> float: return self.bearing_ball_diam + self.bearing_ball_gap + @property - def bearing_radius(self) -> float: - return self.bulk_radius - self.side_thickness - self.bearing_thickness - self.bearing_diam / 2 + def bearing_disk_gap(self) -> float: + diag = self.bearing_ball_diam + dx = self.bearing_gap + return math.sqrt(diag ** 2 - dx ** 2) + + @target(name="bearing-stator", kind=TargetKind.DXF) + def profile_bearing_stator(self) -> Cq.Sketch: + return ( + Cq.Sketch() + .regularPolygon(self.side_width, self.n_side) + .circle(self.bearing_track_radius + self.bearing_gap/2, mode="s") + ) + def bearing_stator(self) -> Cq.Workplane: + return ( + Cq.Workplane() + .placeSketch(self.profile_bearing_stator()) + .extrude(self.bearing_disk_thickness) + ) + @target(name="bearing-rotor", kind=TargetKind.DXF) + def profile_bearing_rotor(self) -> Cq.Sketch: + return ( + Cq.Sketch() + .circle(self.bearing_track_radius - self.bearing_gap/2) + .regularPolygon(self.rotation_radius, self.n_side) + .vertices() + .circle(self.barrel_diam/2, mode="s") + ) + def bearing_rotor(self) -> Cq.Workplane: + return ( + Cq.Workplane() + .placeSketch(self.profile_bearing_rotor()) + .extrude(self.bearing_disk_thickness) + ) + @target(name="bearing-gasket", kind=TargetKind.DXF) + def profile_bearing_gasket(self) -> Cq.Sketch: + pass + + + @target(name="pipe", kind=TargetKind.DXF) + def pipe(self) -> Cq.Sketch: + pass @target(name="side-panel", kind=TargetKind.DXF) def profile_side_panel(self) -> Cq.Sketch: @@ -86,93 +132,53 @@ class Onbashira(Model): # Intersect the side panel return result * intersector - def bearing_channel(self) -> Cq.Solid: - """ - Generates a toroidal channel for the ball bearings - """ - return Cq.Solid.makeTorus( - radius1=self.bearing_radius, - radius2=self.bearing_diam/2, - ) - @target(name="inner-rotor") - def inner_rotor(self) -> Cq.Workplane: - r_outer = self.bearing_radius - base = Cq.Solid.makeCylinder( - radius=r_outer, - height=self.bearing_height - ).translate(Cq.Vector(0,0,-self.bearing_height/2)) - r_rot = self.rotation_radius - channel = self.bearing_channel() - return ( - Cq.Workplane() - .add(base - channel) - .faces(">Z") - .workplane() - .polygon( - nSides=self.n_side, - diameter=2 * r_rot, - forConstruction=True - ) - .vertices() - .hole(self.barrel_diam) - ) - return base - channel - @target(name="outer-rotor") - def outer_rotor(self) -> Cq.Workplane: - polygon_radius = (self.bulk_radius - self.side_thickness) / math.cos(math.radians(self.angle_side / 2)) - profile = ( - Cq.Sketch() - .regularPolygon( - r=polygon_radius, - n=self.n_side, - ) - ) - inner = Cq.Solid.makeCylinder( - radius=self.bearing_radius, - height=self.bearing_height, - ) - base = ( - Cq.Workplane() - .placeSketch(profile) - .extrude(self.bearing_height) - .cut(inner) - .translate(Cq.Vector(0,0,-self.bearing_height/2)) - .cut(self.bearing_channel()) - ) - r = self.bearing_radius * 2 - subtractor = Cq.Solid.makeBox( - length=r * 2, - width=r * 2, - height=self.bearing_height, - ).translate(Cq.Vector(-r, -r, -self.bearing_height)) - return base - subtractor + + def bearing_ball(self) -> Cq.Solid: return Cq.Solid.makeSphere(radius=self.bearing_ball_diam/2, angleDegrees1=-90) - def assembly_bearing(self) -> Cq.Assembly: + def assembly_rotor(self) -> Cq.Assembly: + z_lower = -self.bearing_disk_gap - self.bearing_disk_thickness a = ( Cq.Assembly() .addS( - self.inner_rotor(), - name="inner", - material=self.material_bearing, - role=Role.ROTOR, - ) - .addS( - self.outer_rotor(), - name="outer", + self.bearing_stator(), + name="stator1", material=self.material_bearing, role=Role.STATOR, + loc=Cq.Location(0, 0, self.bearing_disk_gap/2) + ) + .addS( + self.bearing_rotor(), + name="rotor1", + material=self.material_bearing, + role=Role.ROTOR, + loc=Cq.Location(0, 0, self.bearing_disk_gap/2) + ) + .addS( + self.bearing_stator(), + name="stator2", + material=self.material_bearing, + role=Role.STATOR, + loc=Cq.Location(0, 0, z_lower) + ) + .addS( + self.bearing_rotor(), + name="rotor2", + material=self.material_bearing, + role=Role.ROTOR, + loc=Cq.Location(0, 0, z_lower) ) ) - for i in range(self.n_side): + for i in range(self.n_bearing_balls): ball = self.bearing_ball() + loc = Cq.Location.rot2d(i * 360/self.n_bearing_balls) * Cq.Location(self.bearing_track_radius, 0, 0) a = a.addS( ball, name=f"bearing_ball{i}", material=self.material_bearing_ball, role=Role.BEARING, - loc=Cq.Location.rot2d(i * 360/self.n_side) * Cq.Location(self.bearing_radius, 0, 0), + loc=loc, ) return a @@ -180,12 +186,13 @@ class Onbashira(Model): a = Cq.Assembly() side = self.side_panel() r = self.bulk_radius - for i in range(6): + a = a.add(self.assembly_bearing(), name="bearing") + for i in range(self.n_side): a = a.addS( side, name=f"side{i}", material=self.material_side, role=Role.STRUCTURE | Role.DECORATION, - loc=Cq.Location(0,0,0,0,i*60,0) * Cq.Location(0,0,-r) + loc=Cq.Location.rot2d(i*360/self.n_side) * Cq.Location(-r,0,0,90,0,90), ) return a