-T.K.- Lab Notes
  • Home
  • Convention Used
  • STM32
    • Getting Started - STM32 Edition
      • Setting up STM32CubeIDE
      • Going Through A Starter Project
      • Changing STM32CubeIDE Settings
      • Pinout Quick Reference
    • Misc
      • Using Nucleo STLink to Flash Off-board Chips
      • Changing STM32 Default Boot Option
      • STM32 Flash Option Byte Recovery
      • STM32 Systick and Timeout in Interrupt Routines
      • Telesky ST-Link V2 Upgrade Firmware
      • Some Performance Measurements on STM32 MCUs
    • System Core
      • Using GPIO on STM32
      • Setting up External Interrupt on STM32
    • Analog
      • Using ADC on STM32
      • ADC Reading Sequence with DMA on STM32
      • Using OPAMP on STM32
      • Using DAC on STM32
    • Timers
      • Using RTC on STM32
      • Using TIM on STM32
    • Connectivity
      • UART
      • USART
        • USART - CAN Dongle (Fixed Size Serializer with Robust Timeout Handling)
      • CAN
      • FDCAN
      • I2C
      • SPI
        • SPI - GC9A01A LCD Screen
        • SPI - RFID
        • SPI - SD Card
      • Ethernet
        • Ethernet - LWIP
        • Ethernet - UDP
        • Ethernet - UDP Multicast
      • USB - FS
      • USB - HS
    • Middleware
      • FreeRTOS
    • Software Pack
      • STMicroelectronics.X-CUBE-AI - Sine Approximator
  • RISC-V / SoC
    • RISC-V: Baremetal From The Ground Up (Chipyard Edition)
    • Quick Start With Chipyard on Ubuntu or WSL
    • PPA
    • Other Chipyard Stuff
      • Debugging OsciArty with JTAG and command line GDB
      • Debugging BearlyML with JTAG and GDB
      • Booting BearlyML With External SPI Flash
      • Setting Up SD / microSD Card for vcu118 Linux Image
      • More Chipyard Stuff
    • A Minimal Chisel Development Environment with Mill
    • Vivado Stuff
      • Installing Xilinx Vivado on Ubuntu 22.04 / 24.04
      • Arty 35T / 100T UART Pins
      • Configuring Vivado DDR MIG on Arty 35T
      • Configuring Vivado DDR MIG on Nexys Video
      • Vivado Generate Flash Config .mcs File From Bitstream
      • Vivado TCL Scripts
    • Adding Custom Instructions to RISC-V GCC Toolchain
    • Kendryte K230 Bringup
      • K230 EVB Board Resource Overview
    • Setting up RISC-V Toolchain on Ubuntu 24.04/22.04
    • Getting Started with Zephyr
      • Getting Start with Zephyr on RISC-V System - Windows
      • Getting Started with Zephyr on RISC-V - Ubuntu
    • C Library Compile Magic
    • Setting up ExecuTorch on Ubuntu 22.04
      • Executorch on ARM
  • Motor Control
    • Recoil FOC Motor Controller
      • 0x00. Theory of Operation
      • 0x01. Components
      • 0x02. Implementation
      • 0x03. Application
    • Recoil Documentation
    • New Controller Board Soldering & Power-on Checklist
    • MJBOTS Moteus setup
    • Failed Attempt on Acceleration- and Velocity-Limited Trajectory Generation
    • Moteus Code Analyze
    • MIT Motor Controller Code Analyze
    • ODrive Setup
    • Setting up Recoil USB-CAN Adapter
      • Setting up Recoil USB-CAN Adapter - Ubuntu
      • Setting up Recoil USB-CAN Adapter - Windows
    • NTC Temperature Sense Resistor Value Calculation
  • ML/RL
    • Setting up NVIDIA Tools
      • Setting up NVIDIA Driver on Ubuntu 22.04 / 20.04
      • Getting Started with NVIDIA Isaac Lab on Ubuntu 22.04 / 24.04
      • Setting up Omniverse on Ubuntu 24.04 (2025 Ver)
      • Creating Custom Training Environment in IsaacLab via Extensions
      • NVIDIA Isaac Gym URDF Import Notes
      • Setting up TensorRT Environment on Ubuntu 22.04 / 20.04
      • Setting up NVIDIA Omniverse Isaac Sim on Ubuntu 22.04 / 20.04
      • Setting up NVIDIA Nsight System and Nsight Compute on Ubuntu 24.04
      • Getting Started with Jetson AGX Orin
        • Getting Started with Jetson Using SDK Manager on Ubuntu 22.04
        • Using Jetson AGX Orin with Provided Ubuntu 20.04 System
        • Setting up Common Software on Jetson AGX Orin
        • Solving USB-CAN and USB CH340 Driver Issue on reComputer Mini J4012
        • [Deprecated] Upgrading Jetson AGX Orin to Ubuntu 22.04
      • Solving Torch Errors
      • [Deprecated] Setting up NVIDIA Isaac Gym on Ubuntu 22.04 / 20.04
    • RL Frameworks
      • Case Study: A Dive Into LeggedGym and RSL-RL Framework
      • Case Study: A Dive Into IsaacLab
      • Getting Started with Mujoco
      • Case Study: A Dive Into Unitree-Mujoco
      • Case Study: Setting up Berkeley Humanoid
      • Case Study: Looking into robot_lab
      • Case Study: Setting up RL-SAR
      • Case Study: Getting Started with LeRobot
      • Case Study: No-Mercy Project
        • Python Mouse and Keyboard Interaction in Game Environment
        • Detecting Phara
      • OpenAI gym + Mujoco Setup
      • Gazebo Setup
    • ROS
      • Setting up ROS on Ubuntu 20.04
      • Setting up ETH ANYbotics/elevation_mapping on Ubuntu 20.04
    • ROS 2
      • Setting up ROS 2 Humble Hawksbill on Ubuntu
      • Setting up ROS 2 Humble Hawksbill on Windows 10
      • ROS 2 Issue in Ubuntu with conda
    • Google Colab
      • Colab Resource Options
      • so-vits-svc 4.0: Colab Flow
    • URDF to MJCF Mujoco Notes
    • OnShape to URDF
    • Audio Stuff
      • Microsoft TTS
      • GPTSoVITS
      • 深入浅出理解 So-VITS-SVC 原理
      • NAI-SVC Experiment Log
      • Setting up ChatTTS on Ubuntu 22.04
    • Setting up AnythingLLM on Ubuntu 22.04
    • Setting up MineDojo Environment
    • Processing the SFU Motion Capture Dataset
    • Torch Profiling
    • Setting up Unitree A1
  • 3D Modeling
    • 3D Print Tolerancing
    • Blender to OnShape Workflow
    • Onshape to Blender Workflow
    • Setting up FBX Plugin for Python on Ubuntu 22.04
    • Install Blender on Ubuntu 22.04
    • Blender Python Related
    • VRoid, MMD, Blender Workflow
  • Tools
    • Windows
      • Install WSL 2
      • Install Make on Windows
      • Remove EFI disk partition
      • SAI Color Flip/Color Inversion
      • Microsoft Visual Studio Create Software Signature
      • Connecting the SIGLENT SDS1104X-U Oscilloscope to Computer
      • Using JADENS Thermal Label Printer
      • Getting Started with XBee (ZigBee)
    • Ubuntu
      • Ubuntu 22.04 Standard Installation Procedure
      • Protobuf
      • Setting up Docker on Ubuntu 22.04
      • Linux Mounting SD Card
      • Partitioning SD card
      • Windows Ubuntu Dual Boot Issues
      • Check Disk / Folder / File Size
      • Test Disk Read/Write Speed
      • Cannot Start Chrome in Ubuntu 22.04 After Changing Network Settings
      • Configure USB Access Permissions (udev rules) on Ubuntu
      • Screen Commands
      • Disabling the "<Application> is not responding." System Message on Ubuntu
      • Install and Configure GlobalProtect UC Berkeley VPN Service on Ubuntu 22.04
      • Solving Gamepad not Detected on Ubuntu 22.04
      • Using 3DConnexion Mouse on Ubuntu with Python
      • Install Cursor the AI Editor on Ubuntu 22.04/24.04
      • Solving the .nfsXXX file cannot be deleted issue
      • Windows Remote Desktop Issues
      • nsswitch.conf
    • Lab Automation
    • Github-Related Info
    • Python
      • Publish Python Package to PyPi
      • Python Logging Utility
      • Python converting bettwen JSON and XML
      • Retrieve Github user avatar with Github API
      • Jupyter Notebook Error
    • Raspberry Pi Setup
    • Clang-Format Style Config
    • CrazyFlie Setting Up
    • Using Oscilloscope: x1 vs x10
    • Using the BWRC 3D Printer
    • Using the Leica Microscope at BWRC
    • Pair XBoxController to Raspberry Pi with Bluetooth
    • Reading FrSky Transmitter SBUS data with STM32
    • Configuring the FrSky TARANIS X9D Plus 2019 RC Controller
    • Applying Notion for Education
    • Gitbook Errata
    • Setting up SteamVR without HMD
    • CMake Best Practices
    • Adobe Premiere Pro Audio Level Settings
  • Mechanical
    • MAD Cycloidal Actuator
    • Dog Stuff
      • Fixing the Unitree A1 Robot Dog Leg Motor
      • Fixing the Unitree A1 Robot Dog Ethernet Port
      • Fixing MIT Mini Cheetah
      • Fixing the Unitree Go1 Robot Dog Ethernet Port
    • 3D Printer Profile
  • Electrical
    • A Note on the Polarity of the Famous TT Motor
    • Wiring Pinmap Convention
    • MCU Pinmap Convention
    • PCB Design and Manufacturing Conventions
    • ESP32 Cam
    • LiPo Safety
    • AS5600 Modification
    • OpenOCD and FTDI Chips
    • FT-LINK FTDI Debugger Design Considerations
    • A Study on Reset Pin Connection
    • Note on CAN Termination Resistor
  • UW
    • Digital-Twin Communication System
    • Unreal Engine Communicate with SteamVR
    • Unreal Engine Socket Communication
    • A Note on Coordinate Systems
    • NewLine Serialization Method
    • Humanoid Design Notes
      • Robot Body Ratio Issue
      • VRM Parameters
      • Note on Face Design and Manufacture
  • Workflow Automation
    • RISC-V Toolbox Website
    • Zigbee-Based Home Automation
      • Setting up Home Assistant on Raspberry Pi to Control Zigbee IoT Devices
      • Update Sonoff Zigbee 3.0 USB Dongle Plus (CC2652P)
  • Finance
    • Finance
    • UC Berkeley Reimbursement
  • Life
    • Some Interview Questions
    • Health Insurance
Powered by GitBook
On this page
  • New Method (Updated 2023-09-30)
  • RI-60 Parameters
  • RI-70 Parameters
  • Old Method

Was this helpful?

  1. Mechanical

MAD Cycloidal Actuator

New Method (Updated 2023-09-30)

# generate_cycloidal_profile.py
import numpy as np
import matplotlib.pyplot as plt
import ezdxf


def generateCycloidalGearProfile(r_housing=36, r_roller=4, N=16, eccentricity=2):
    theta = np.arange(0, 2*np.pi, 0.001)

    psi = - np.arctan(np.sin((1 - N) * theta) / (r_housing / (eccentricity * N) - np.cos((1 - N)*theta)))

    x = r_housing * np.cos(theta) - r_roller * np.cos(theta - psi) - eccentricity * np.cos(N * theta)
    y = -r_housing * np.sin(theta) + r_roller * np.sin(theta - psi) + eccentricity * np.sin(N * theta)

    return np.concatenate(([x], [y]), axis=0)


def writeDXF(filename, fit_points, ref_circle_radius=10):
    doc = ezdxf.new("R2010") # create a new DXF drawing in R2010 fromat 
    msp = doc.modelspace()

    fit_points = fit_points.T.tolist()

    start_point = fit_points[0]

    # close the loop
    fit_points.append(start_point)

    spline = msp.add_spline(fit_points)

    spline.set_closed([(start_point[0], start_point[1], 0)])

    print(spline.dxf.n_fit_points)

    circle = msp.add_circle((0, 0), radius=ref_circle_radius)
    
    doc.saveas(filename)

# 5010

fit_points = generateCycloidalGearProfile(
    r_housing=29.,
    r_roller=2.5,
    N=16.,
    eccentricity=1.25
    )
'''
#fit_points_ = generateCycloidalGearProfile(
#    r_housing=29.,
#    r_roller=3.,
#    N=16.,
#    eccentricity=1.25
#    )

'''
# M6C12
'''
fit_points = generateCycloidalGearProfile(
    r_housing=36,
    r_roller=4,
    N=16,
    eccentricity=2
    )
'''
print(fit_points.shape)

x, y = fit_points
#x_, y_ = fit_points_
plt.plot(x, y)
#plt.plot(x_, y_)
plt.show()

writeDXF("cycloidal_profile.dxf", fit_points, ref_circle_radius=31)

RI-60 Parameters

Housing Radius

29

mm

Housing Roller Diameter

5

mm

Central Hole Diameter

18

mm

Side Hole Diameter

13

mm

Eccentricity

1.25

mm

Spacing Radius of Side Hole

17

mm

RI-70 Parameters

Housing Radius

36

mm

Housing Roller Diameter

8

mm

Central Hole Diameter

18

mm

Side Hole Diameter

13

mm

Eccentricity

2

mm

Spacing Radius of Side Hole

20

mm

Old Method

Go in an active sketch plane

Run the following script:


import math

import adsk.core, adsk.fusion, traceback


def generateCycloidalOutline(n_planet_teeth, n_housing_teeth, e, pin_to_center_distance):
    intersectsItself = False
    lastMaxAngle = -math.pi / 2
    
    origin = adsk.core.Point3D.create(0, 0, 0)
    halfAngle = 2 * math.pi / n_planet_teeth / 2
    N = 101

    points = adsk.core.ObjectCollection.create()

    for ip in range(N + 1):
        angle = (2 * math.pi / n_planet_teeth) / N * ip
        x = pin_to_center_distance * math.cos(angle) + e * math.cos(n_housing_teeth * angle)# - e
        y = pin_to_center_distance * math.sin(angle) + e * math.sin(n_housing_teeth * angle)

        p = adsk.core.Point3D.create(x, y, 0)
        points.add(p)

        #currentAngle = Angle.getToPoint(origin, p)
        currentAngle = math.atan2(p.y - origin.y, p.x - origin.x)

        # if not intersectsItself and currentAngle < lastMaxAngle:
        #     intersectsItself = True

        # if intersectsItself and currentAngle <= halfAngle:
        #     break

        lastMaxAngle = currentAngle

    # if intersectsItself:
    #     upperPoints = [adsk.core.Point3D.create(p.x, -p.y, 0) for p in points]
    #     upperPoints = [getRotated(p, Matrix.getForRotation(2 * halfAngle, origin)) for p in upperPoints]
    #     return [points, upperPoints]

    return points


def run(context):
    '''
    ## Etch M6C12 params
    reduction_ratio = 15
    eccentricity = 2
    pin_diameter = 8
    housing_inner_diameter = 72
    '''
    
    ## Etch 5010 params
    reduction_ratio = 15
    eccentricity = 1.5
    pin_diameter = 5
    housing_inner_diameter = 56




    n_planet_teeth = reduction_ratio
    n_housing_teeth = reduction_ratio + 1

    
    eccentricity *= 0.1
    pin_diameter *= 0.1
    housing_inner_diameter *= 0.1

    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        design = adsk.fusion.Design.cast(app.activeProduct)
        if not design:
            ui.messageBox('No active Fusion 360 design', 'No Design')
            return
        rootComp = design.rootComponent
        
        sketch = app.activeEditObject
        

        origin_point = adsk.core.Point3D.create(0, 0, 0)

        points = generateCycloidalOutline(
            n_planet_teeth=n_planet_teeth,
            n_housing_teeth=n_housing_teeth,
            e=eccentricity,
            pin_to_center_distance=housing_inner_diameter/2
            )

        cycloidSpline = sketch.sketchCurves.sketchFittedSplines.add(points)
        
        
        cycloidSplinecoll = adsk.core.ObjectCollection.create()
        
        cycloidSplinecoll.add(cycloidSpline)


        offsetSpline = sketch.offset(cycloidSplinecoll, adsk.core.Point3D.create(), pin_diameter / 2).item(0)
        cycloidSpline.deleteMe()
        
        #endSplinePoint = getRotated(offsetSpline.startSketchPoint.geometry, getForRotation(math.tau / n_planet_teeth, origin_point))
        p = offsetSpline.startSketchPoint.geometry.copy()

        mat = adsk.core.Matrix3D.create()

        mat.setToRotation(math.tau / n_planet_teeth, adsk.core.Vector3D.create(0, 0, 0.5), origin_point)

        p.transformBy(mat)

        endSplinePoint = p
        circularFeatures = [offsetSpline]

        # If profile will be probably open, make more accurate segment
        if not endSplinePoint.isEqualToByTolerance(offsetSpline.endSketchPoint.geometry, 1e-8):
            #line = Sketch.Draw.lines(sketch, [endSplinePoint, offsetSpline.endSketchPoint.geometry])[0]
            points = [endSplinePoint, offsetSpline.endSketchPoint.geometry]
            asConstruction = False
            close = False
            lines = []

            for ip in range(1, len(points)):
                line = sketch.sketchCurves.sketchLines.addByTwoPoints(points[ip - 1], points[ip])
                line.isConstruction = asConstruction
                lines.append(line)

            if close:
                line = sketch.sketchCurves.sketchLines.addByTwoPoints(points[len(points) - 1], points[0])
                line.isConstruction = asConstruction
                lines.append(line)

            circularFeatures.append(lines[0])
            

        #Sketch.Pattern.circular(sketch, circularFeatures, n_planet_teeth, origin=origin_point)   
        sketchObjectOrObjects = circularFeatures
        quantity = n_planet_teeth
        angle = 2 * math.pi
        origin = origin_point

        objects = adsk.core.ObjectCollection.create()
        for o in sketchObjectOrObjects:
            objects.add(o)

        #objects = getCollection(sketchObjectOrObjects)

        for step in range(quantity - 1):
            stepAngle = angle / quantity * (step + 1)
            
            transformMatrix = adsk.core.Matrix3D.create()

            transformMatrix.setToRotation(stepAngle, adsk.core.Vector3D.create(0, 0, 0.5), origin)
            sketch.copy(objects, transformMatrix, sketch)

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

Then we get the cycloidal gear profile

Pins and housing
════════════════
  rr     28.0000 mm
  dr      5.0000 mm
  zr          16

Planets(s)
══════════
  dc      8.0000 mm
  ds     13.0000 mm
   e      1.5000 mm
  df     48.0000 mm
  da     54.0000 mm
  tp      3.5000 mm
   N           2
  ap      0.5000 mm

Input
═════
  dc      8.0000 mm
   e      1.5000 mm
  tp      3.5000 mm
   N           2
  ap      0.5000 mm

Output
══════
  rb     16.0000 mm
  db     10.0000 mm
  zs           6
  to      1.0000 mm
  ao      0.5000 mm

To adapt to the motor we are using, we need to edit the model a bit.

Last updated 1 year ago

Was this helpful?

5010 EEE 110 KV V2.0MAD Components Drone brushless Motor for multirotor Quadcopter Drone­Amazon.com
Logo