Skip to content

cycloidgen v7.3.0

Choose a tag to compare

@github-actions github-actions released this 12 Aug 10:19
· 14 commits to main since this release

Numbers

  • The disc turns against the crank, and the app had them subtracting. Every
    speed measured between the disc and the crank was computed as
    input_rpm * (1 - 1/i). It is 1 + 1/i: the disc rotates the opposite way
    from the crank — that is what a fixed-ring cycloidal drive is — so the two
    rates add rather than cancel.

    Three places carried it. The eccentric cam bearing, which separates those
    two bodies and so turns at their difference; the output pin rubbing speed,
    which is the disc walking round the carrier at that same rate; and the
    output stage's sweep period, derived from how fast the eccentricity
    direction runs round as seen from the carrier.

    What gave it away was the pin-in-hole constraint. Relative to the carrier the
    disc translates on a circle of radius E, so at every crank angle each output
    pin has to sit exactly E from its hole centre — and it does, only with the
    carrier turning at +phi/N. Put that rate in and the eccentricity direction
    seen from the carrier advances at (N+1)/N, not (N-1)/N. The two stage
    periods then agree for the first time: the output period comes out as the ring
    period divided by the output pin count, which is what a pattern of n repeats
    in one turn of the ring pattern has to be. They were derived independently and
    only the correct rate makes them meet.

    Measured over the presets:

    Quantity 10:1 21:1 59:1
    Cam bearing speed, output PV, output sliding speed +22.2% +10.0% +3.5%
    Cam bearing L10 life −11% −9.1% −3.3%
    Input shaft support speed +10.0% +4.8% +1.7%
    Running temperature +4.2% +1.7% +0.3%
    Input torque for the rated output +1.7% +1.0% +0.4%
    Efficiency −1.2 pt −0.7 pt −0.3 pt
    Torsional stiffness +2.6% +4.5% +5.6%

    It is worst where it matters most — a low ratio is where that bearing is
    fastest — and everything on the ring side is unchanged, which is the expected
    result rather than a reassuring one: nothing about the ring contact was ever
    measured against the crank.

  • The two input shaft supports do not turn at the same speed, and both were
    quoted at the input speed. One sits in an end plate and one in the carrier's
    boss, and those two bodies move differently. The schedule still carries one
    row — the seats are the same size, so one part number does for both — but it
    is sized on the faster of the two and prints both.

  • The output pin rollers were sized against the input speed, which is
    neither the speed of anything at that contact nor the frequency of anything.
    They are now counted on the rate the hole walks round the pin, which is the
    cycle they are actually loaded on.

Which member is the output

  • Either of the two slow members can now be the output. A cycloidal drive is
    a three-shaft machine: the crank is the input, and the ring and the carrier are
    interchangeable. Ground the ring and the carrier turns at N:1, reversed —
    which is what the app has always built. Ground the carrier and the housing
    turns, at N+1:1, in the same direction as the input. Same parts, one more
    tooth of reduction, and it is what most printed micro drives are.

    output_member selects it, and everything downstream follows rather than
    being told twice:

    • The reduction and the direction come off the two members' rotation rates, so
      ratio cannot disagree with the picture drawn from the same numbers.
    • Every rate in the app is now stated per unit input speed rather than per
      unit crank angle, because those part company here: with the carrier
      grounded the crank runs at (N+1)/N of the input. Relative speeds inside
      the drive are unchanged by the choice, which they must be — grounding a
      member adds one rigid rotation to all of it at once.
    • The motor moves to whichever member stands still. On a ring-output drive
      the carrier grows a base at the end of its boss, carrying the motor's
      pattern and register; the input end plate loses them and gains an output
      bolt circle for the driven machine instead, on the tie bolts' own circle
      and half a pitch round from them.
    • The 3D view, the 2D mechanism view and the exported animation turn the part
      that actually turns. One rigid frame rotation applied to the whole assembly,
      not a second set of motion laws to keep in step with the first.
    • The design search knows the difference between a reduction and a lobe count:
      a 30:1 off the ring is a twenty-nine lobe disc.
  • A ring-output drive is a frame, not a plate. The grounded member cannot be
    a carrier hanging on six cantilevered pins with a barrel swinging off one
    bearing, and the reference builds are not: the pins land in an end cap at
    their far end and become the frame's own fasteners, so the carrier, the pins
    and the cap are one rigid cage that the housing turns inside. That is a new
    made part, end_cap, with its own STEP, STL and line on the bill of
    materials, and it changes two numbers that matter.

    The output pins are beams. A pin built into one plate carries F*L; the
    same pin caught at both ends carries F*a*b/L. On the 21:1 preset with two
    discs that is 45.3 MPa of fully reversed bending against 19.9 MPa. Their
    stiffness moves the other way and the app says so - the span is now the
    whole pin rather than half a stack, so the structure comes out slightly softer
    (7.26 to 6.57 Nm/arcmin) even though the case is stiffer.

    The housing is carried at both ends. One bearing locates a barrel and does
    not hold one against a moment, which is exactly what a wheel or a pulley on a
    turning barrel applies. There is a main output bearing on each of the frame's
    two bosses now, and both shaft supports move inside those bosses - the one
    that used to sit in the input end plate had to, because that plate turns.

    It costs length: the barrel has to cover the cap, so the 21:1 preset goes from
    40 mm to 63 mm and from 781 g to 1033 g.

  • The output pin's bending arm starts at the carrier's face, which is a
    carrier drop below the first disc, and it was being measured from the disc.
    Small - one millimetre on a moment arm - but it is a moment arm, and it pushed
    a 16 mm disc stack on the 15:1 preset from just inside the output pin's
    fatigue limit to just outside it. FATIGUE_LIFE says so now. Carrier-output
    drives are affected, which is all of the ones that existed before this
    release.

  • The carrier's base was drawn one millimetre off the boss it stands on.
    Introduced with the base itself, earlier in this release, and never shipped:
    the part is modelled in its own frame and an assembled height was used in it
    without the carrier drop, so the exported solid was in two pieces. The volume
    was right, which is why nothing caught it - a printer would have made both.

  • OUTPUT_BOLT_CLASH, a new check. The output face's bolts share a circle
    with the tie bolts, because that is the one radius on that plate with barrel
    wall behind it to thread into. Equal counts interleave exactly; seven against
    six leaves 0.12 mm of metal; twelve against six lands one hole on another.
    Fifty-six checks.

Ring pins the housing is printed with

  • The ring pins can be formed with the housing instead of fitted into it as
    separate dowels — ring_pins_integral, and the case every printed drive is.
    A pocket and the pin that fills it are one shape read from either side, so
    almost the whole of the difference is which arc of the same circle the bore
    follows, the outward half or the inward one, and cut against union in the
    exporter.

    The pins stop being a part when they are integral: no body in the 3D view, no
    STL, no line on the bill of materials — twelve lines to eleven, six bought
    parts to five — and no visibility row for something you can neither see
    separately nor take out. Their mass moves into the barrel and into the
    housing's material, which is the point of the option: on the 21:1 preset the
    drive goes from 781 g to 623 g, because 188 g of steel dowels become 30 g of
    the printed material that was going to be hollowed out to seat them.

  • They also stop being able to roll, and that is not free. An integral pin
    cannot turn in a pocket it is part of, so the drive that pays for this is the
    one that had rolling ring pins. Same 21:1, rollers on:

    Quantity Rolling dowels Formed with the housing
    Efficiency 83.7% 70.4%
    Ring pin loss 1.00 W 6.63 W
    Running temperature 29.5 °C 40.5 °C
    Ring pin sliding duty rolls 2.2x the PV limit

    PV_LIMIT_RING fires there rather than the trade being made quietly: PLA on
    steel at 0.22 MPa and 0.30 m/s is a disc that wears round long before it
    breaks. The ring pin roller leaves the bearing schedule with it, five rows to
    four, which is the other half of the same fact — there is no longer a part
    free to turn for a needle to sit under.

  • ring_pins_roll is what every consumer asks now, and it is derived rather
    than enforced. The obvious way is a validator that clears
    ring_pins_are_rollers when the pins are integral, and it does not work:
    model_copy runs none, so a spec that arrived that way kept rolling pins on
    an integral ring and its efficiency came back unchanged — which is how this
    was caught. Deriving it also keeps the roller preference for when the pins
    stop being integral, which is why the box is greyed rather than cleared. The
    field is in mesh_fingerprint for the same class of reason: it changes the
    mesh, and a key that did not carry it would have served the old one.

Added

  • 3MF exportassembly.3mf, in the solids group. The STL entry in the
    manifest has always carried its own complaint: STL has no assembly structure
    and no colour, so a multi-disc stack arrives as separate files
    . Every one of
    those is a thing the app knows and the file cannot say. This is the same
    triangles with the sentence finished — one container, every part where it
    assembles, in the colour the 3D view paints it and named for the material the
    bill of materials orders it in. Identical discs are one object placed twice
    and different discs are two objects, which is the fact an STL folder can only
    state in its file names. Made parts only: a mesh of a bearing is a fit check
    at best and something someone tries to print at worst.

    Two things 3MF asks for that STL does not. The shells have to be closed.
    OCCT triangulates face by face and each face brings its own copy of the points
    along its edges, so the raw tessellation is the same heap of loose facets the
    3D view turned out to be, and the points are merged on a nanometre grid before
    anything is written — which halves the vertex count as a side effect. And
    they have to be wound outwards
    , which is checked as a signed volume against
    the solid: every part lands within 0.1% of the body it stands for, short
    wherever the surface is convex and — on the discs alone — a whisker long,
    because a chord across a concave flank falls outside the surface it is
    approximating.

    The tessellation is the STLs' own, at the same tolerance, so two files of one
    part cannot disagree about its geometry, and the placements are the STEP
    assembly's. The whole drive is lifted onto the build platform by one
    translation, because 3MF puts the plate at z = 0 and this gearbox is
    modelled around its disc stack, with the carrier hanging below it. Exporting
    the same design twice gives the same bytes.

    The one check the suite cannot make is whether a slicer opens it, so that was
    made by hand: Bambu Studio's command line reads the file back as eight named
    objects, and each one's volume matches the solid it was tessellated from.

Fixed

  • Integral ring pins were promised as two files that nothing wrote.
    step/ring_pins.step and stl/ring_pins.stl stayed in the manifest after the
    pins became part of the housing. The exporter had it right — there is no pin
    to make — so the Outputs tab listed two files, --list-outputs printed them,
    and a double-click opened nothing. The check that compares the declaration
    against what lands on disk is the one this project relies on for exactly this,
    and it had only ever been pointed at a drive with dowels.

  • The last two parts that were not watertight. 7.2.0 closed twelve of the
    fourteen and named the two it had not, which were separate faults.

    output_flange had a wall buried in it. The carrier plate and the boss below
    it are separate prisms that meet at the plate's underside, and each kept the
    face it meets on, so four surfaces shared the bore ring. Only the annulus
    outside the boss is a face of the part; the rest is interior to the union and
    is no longer emitted. prism takes cap_bottom/cap_top for it, and there
    is a ring for the annulus that is left — both of which any other stacked
    pair will want.

    disc_1 had a four-edge hole in its top face. vtkContourTriangulator
    stopped part way and said nothing, and any output with triangles in it was
    accepted, so the face came back 0.93% short. The area is checked against the
    loops it was given now, and a short face is retried with the plane turned,
    through angles that are not multiples of one another. The retry keeps the
    connectivity and throws the rotated coordinates away: rotating and unrotating
    would move every point by a rounding error, and these points have to merge
    exactly with the wall vertices that share them, which is what closes the
    surface in the first place.

    Every part of every preset is watertight, so the section plane caps all of
    them rather than twelve of fourteen, and three tests hold it — no holes and no
    non-manifold edges, every face triangulated to its whole area, and the
    assumption the retry stands on, that the triangulator hands back the points it
    was given in order. test_every_part_is_a_closed_surface passed throughout
    and was not wrong: it weighs the mesh's own facet loops, and those cancel
    whether or not anything managed to fill them.

  • A face with two bolt circles in it could come back triangulated wrong, and
    the check that was meant to catch it could not see the failure. Faces are
    filled by vtkContourTriangulator and verified against the area they should
    have — but a plate carrying fourteen loops came back with its area exact to
    rounding and a triangle missing, because the triangulator had also emitted a
    different one twice and the two errors cancelled in a sum of absolute areas.
    The acceptance test now asks the topology directly: every edge either on a
    loop or shared by exactly two triangles. Four more retry angles came out of a
    search over every distinct multi-hole face the app can draw.

  • The motor's register left a wall inside the input end plate. Where a
    spigot is wider than the bore under it the plate is built as two prisms, and
    both were capped on the face where they meet — so the plate had a full annular
    wall buried in it, every bolt hole crossing that wall was non-manifold, and
    the section plane could not cap the one part a motor bolts to. Only the step
    between the two bores is exposed, and only that is emitted now. It affected
    any frame piloting wider than the shaft support seat, which is a NEMA 23 or 34
    on a default drive.

  • The carrier's base is weighed. A made part the mass model has not been
    told about is a gearbox that weighs less on paper than in your hand.


Windows - cycloidgen_..._Setup.exe. It is unsigned, so
SmartScreen will warn on first run: More info -> Run anyway. A
signed build is planned.

Linux - cycloidgen-...-x86_64.AppImage. chmod +x and run it;
it needs glibc 2.35 or newer. If it will not start, the machine has no
FUSE 2: install libfuse2, or run the file with
--appimage-extract-and-run, which unpacks it instead of mounting it.

macOS - cycloidgen-...-arm64.dmg, Apple silicon. Drag it to
Applications, then run this before opening it:

xattr -dr com.apple.quarantine /Applications/cycloidgen.app

It is signed ad-hoc but not notarized, so Gatekeeper blocks the first
launch. On macOS 15 and later the dialog it raises defaults to
Move to Trash, which deletes the install - there is no Open
Anyway
in that dialog, it appears in System Settings only after a
launch has been blocked, and right-click -> Open no longer bypasses
it. Clearing the flag first avoids the dialog entirely. On an Intel
Mac, use pip.

Anywhere with Python 3.10-3.12: pip install cycloidgen, then
cycloidgen.

The numbers this produces are preliminary sizing estimates, not a
certification. Validate against a physical prototype before anything
load-bearing depends on them.