diff --git a/main.py b/main.py new file mode 100644 index 0000000..edd3bac --- /dev/null +++ b/main.py @@ -0,0 +1,278 @@ +import streamlit as st +from math import gcd + +# ---------------------------- +# Helpers +# ---------------------------- +def simplify_fraction(n: int, d: int) -> tuple[int, int]: + if d == 0: + raise ValueError("Denominator cannot be 0.") + if n == 0: + return 0, 1 + g = gcd(abs(n), abs(d)) + n //= g + d //= g + # Keep denominator positive + if d < 0: + n *= -1 + d *= -1 + return n, d + + +def scale_down_to_limit(n: int, d: int, limit: int) -> tuple[int, int]: + """ + If numerator/denominator exceed 'limit', scale them down proportionally. + Keeps ratio approximately the same (exact if divisible), then re-simplifies. + """ + if n == 0: + return 0, 1 + n_abs, d_abs = abs(n), abs(d) + if n_abs <= limit and d_abs <= limit: + return n, d + + # Scale by the maximum overflow factor + factor = max((n_abs + limit - 1) // limit, (d_abs + limit - 1) // limit) + n2 = n // factor + d2 = d // factor + + # Avoid zeroing out + if n2 == 0: + n2 = 1 if n > 0 else -1 + if d2 == 0: + d2 = 1 + + return simplify_fraction(n2, d2) + + +# ---------------------------- +# Page config + nav +# ---------------------------- +st.set_page_config(page_title="Delta ASDA-A2 Engineering Tool", layout="wide") + +st.sidebar.title("Configuration") +page = st.sidebar.radio( + "Go to", + ["Theory & Architecture", "PUU Calculator", "Parameter Guide", "ASDA-Soft Guide"], +) + +# ---------------------------- +# Pages +# ---------------------------- +if page == "Theory & Architecture": + st.title("πŸ“š Theory & Architecture") + st.markdown( + """ +This tool helps you set up **electronic gearing (PUU / P1-44 & P1-45)** and the **core safety/tuning parameters** +for a system with a gearbox (e.g., **50:1**). + +### Concept (simple) +Most motion controllers output **position command pulses**. +The servo drive converts those pulses into **motor encoder counts** using an **electronic gear ratio**: + +\[ +\\text{Motor encoder counts} = \\text{Command pulses} \\times \\frac{P1-44}{P1-45} +\] + +So you choose **P1-44 / P1-45** to match *your desired β€œuser unit”* (degrees, mm, etc.) at the **load side**. + +### With a gearbox +- Motor counts per **load revolution** = *(motor encoder counts per motor rev)* Γ— *(gear ratio)* +- If you want e.g. **360 units per load rev** (degrees), then: + +\[ +\\frac{P1-44}{P1-45} = \\frac{\\text{motor counts per load rev}}{\\text{units per load rev} \\times \\text{controller pulses per unit}} +\] + +Use the **PUU Calculator** page to get clean integers. + """ + ) + +elif page == "PUU Calculator": + st.title("πŸ”’ Universal PUU Calculator (P1-44 / P1-45)") + + st.caption( + "Goal: convert your controller command pulses into the correct motor encoder counts, " + "including gearbox ratio, and optionally define a meaningful user unit (deg/mm/etc.)." + ) + + col1, col2 = st.columns(2) + + with col1: + st.subheader("Input") + motor_counts_per_rev = st.number_input( + "Motor encoder counts per motor revolution", + min_value=1, + value=128000, + step=1000, + help="Common values: 128000, 131072, 1048576 ... depends on encoder/resolution & drive setting.", + ) + + gear_ratio = st.number_input( + "Gear ratio (motor rev per load rev)", + min_value=1.0, + value=50.0, + step=1.0, + help="For 50:1 reduction, motor turns 50 rev while load turns 1 rev => enter 50.", + ) + + units_per_load_rev = st.number_input( + "User units per load revolution", + min_value=1, + value=360, + step=1, + help="Examples: degrees=360, turns=1, mm per rev for a leadscrew (e.g. 5 mm/rev => 5).", + ) + + controller_pulses_per_unit = st.number_input( + "Controller command pulses per user unit", + min_value=1.0, + value=1.0, + step=1.0, + help="If your controller outputs 1 pulse = 1 user unit, keep 1. " + "If it outputs e.g. 10 pulses per degree, enter 10.", + ) + + ratio_limit = st.number_input( + "Max integer limit for P1-44 / P1-45 (safety)", + min_value=1000, + value=2_147_483_647, + step=1000, + help="Keeps numbers within typical 32-bit signed range. " + "If you know the drive's tighter limit, set it here.", + ) + + # Calculate + # motor_counts_per_load_rev = motor_counts_per_rev * gear_ratio + # Desired motor counts per controller pulse = motor_counts_per_load_rev / (units_per_load_rev * controller_pulses_per_unit) + motor_counts_per_load_rev = motor_counts_per_rev * gear_ratio + denom_units = units_per_load_rev * controller_pulses_per_unit + + st.divider() + + # Convert to integer fraction: + # We want P1-44/P1-45 = motor_counts_per_load_rev / denom_units + # If denom_units isn't integer, scale to preserve precision. + # Use 1e6 scaling for decimals, then simplify. + SCALE = 1_000_000 + n_raw = int(round(motor_counts_per_load_rev * SCALE)) + d_raw = int(round(denom_units * SCALE)) + + n_s, d_s = simplify_fraction(n_raw, d_raw) + n_final, d_final = scale_down_to_limit(n_s, d_s, int(ratio_limit)) + + with col2: + st.subheader("Result") + st.success("Recommended electronic gear ratio for PUU:") + + st.markdown( + f""" +- **P1-44 (Numerator / N):** **{n_final}** +- **P1-45 (Denominator / M):** **{d_final}** + """ + ) + + # Show what it means + pulses_to_motor_counts = n_final / d_final + st.write(f"βœ… This means: **1 controller pulse β†’ {pulses_to_motor_counts:.6f} motor encoder counts**") + + # Derived helpful values + motor_counts_per_user_unit = motor_counts_per_load_rev / units_per_load_rev + controller_pulses_per_load_rev = units_per_load_rev * controller_pulses_per_unit + motor_counts_per_controller_rev = controller_pulses_per_load_rev * pulses_to_motor_counts + + st.markdown("### Sanity checks") + st.write(f"- Motor counts per **load rev**: **{motor_counts_per_load_rev:.3f}**") + st.write(f"- User units per **load rev**: **{units_per_load_rev:.3f}**") + st.write(f"- Motor counts per **user unit** (load side): **{motor_counts_per_user_unit:.3f}**") + st.write(f"- Controller pulses per **load rev**: **{controller_pulses_per_load_rev:.3f}**") + st.write(f"- Motor counts generated by **controller pulses per load rev**: **{motor_counts_per_controller_rev:.3f}**") + + st.info( + "Tip: The last line should match (or be very close to) motor counts per load rev. " + "If it's far off, check encoder counts, gear ratio, and pulses-per-unit." + ) + st.info( + "Recommended Minimum: It is generally recommended to keep the PUU per motor revolution above 5000 to avoid 'stepping' or vibration at low speeds." + ) + st.info( + f"Electronic Gear Ratio Limit: Delta A2 allows a ratio between $1/50$ and $25600$. " + f"Your ratio of {motor_counts_per_load_rev}/{units_per_load_rev} β‰ˆ {motor_counts_per_load_rev / units_per_load_rev:.3f} " + f"is well within this safe range." + ) + +elif page == "Parameter Guide": + st.title("πŸ› οΈ Essential Parameter Setup") + st.write("Beyond the PUU (P1-44/45), these parameters are critical for a 50:1 gearbox system.") + + st.subheader("1. The 'Big Three' for Gearbox Safety") + st.markdown( + """ +| Parameter | Name | Recommended Value | Why? | +| :--- | :--- | :--- | :--- | +| **P1-44** | Numerator (N) | *From Calculator* | Sets electronic gear / user units. | +| **P1-45** | Denominator (M) | *From Calculator* | Sets electronic gear / user units. | +| **P1-37** | Load to Motor Inertia | **10.0 ~ 20.0** | A 50:1 gearbox increases reflected inertia significantly. Start here for tuning. | + """ + ) + + st.subheader("2. Speed & Torque Limits") + st.info( + "With 50:1 reduction, output torque is multiplied ~50Γ— while output speed is divided ~50Γ—. " + "Use limits to protect mechanics during first tests." + ) + + st.markdown( + """ +- **P1-02 (Speed Limit):** Set to motor-rated safe speed (e.g., 3000 RPM) to prevent overspeed. +- **P1-12 / P1-13 (Torque Limit):** Start conservative (e.g., 30–60%) during first motion tests to avoid smashing end-stops. + """ + ) + + st.divider() + + st.subheader("πŸ”„ Factory Reset (Set to Default)") + st.warning("Warning: Resetting will erase all PUU and tuning settings!") + + col_a, col_b = st.columns(2) + with col_a: + st.markdown("#### Via Drive Keypad") + st.code( + """ +1. Go to Parameter P2-08. +2. Set value to 10. +3. Press 'SET'. +4. Power Cycle (Off/On). + """.strip(), + language="markdown", + ) + + with col_b: + st.markdown("#### Via ASDA-Soft") + st.markdown( + """ +1. Open **Parameter Editor**. +2. Click **Initial** / **Factory Default**. +3. Confirm and click **Write**. +4. Restart the Drive. + """ + ) + +elif page == "ASDA-Soft Guide": + st.title("πŸ’Ύ Implementation in ASDA-Soft") + st.markdown( + """ +To apply your **P1-44** and **P1-45** values: + +1. Open **Parameter Editor** +2. Select **Group 1** (Basic Parameters) +3. Enter **P1-44** and **P1-45** +4. Click **Write to Drive** +5. **Power cycle / restart** the drive if required for the change to take effect + +### Quick workflow +- Calculate PUU in the **PUU Calculator** +- Set **P1-44 / P1-45** +- Set safe limits (**P1-02**, **P1-12**, **P1-13**) +- Then tune (**P1-37** and auto-tuning if you use it) + """ + ) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4d74b08 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +asarPy==1.0.1 +Cython==3.2.1 +Mako==1.3.10.dev0 +Markdown==3.10 +MarkupSafe==3.0.3 +meson==1.9.1 +packaging==25.0 +setuptools==80.9.0 +wheel==0.46.1