import gradio as gr import numpy as np import plotly.graph_objects as go from plotly.subplots import make_subplots # ── Robot database ──────────────────────────────────────────────────────────── # speed_ratio: task throughput relative to an average human worker (1.0 = same # speed). A ratio of 0.5 means the robot takes twice as long per task, so it # delivers half the output per operating hour even at full accuracy. # Sources: HumanoidBench, manufacturer pilot reports, Open X-Embodiment (2024-25). ROBOTS = { "Tesla Optimus Gen 2": { "price_usd": 30000, "power_w": 500, "battery_h": 10, "maintenance_pct": 0.10, "lifespan_yr": 8, "task_accuracy": 0.65, "speed_ratio": 0.55, "benchmark": "Open X-Embodiment / HumanoidBench", "notes": "General-purpose; 2.3 kWh battery; commercial scale target price.", }, "Unitree G1": { "price_usd": 16000, "power_w": 400, "battery_h": 2, "maintenance_pct": 0.20, "lifespan_yr": 5, "task_accuracy": 0.55, "speed_ratio": 0.40, "benchmark": "HumanoidBench (locomotion tasks)", "notes": "Consumer-grade entry point; limited dexterity; short battery life.", }, "Unitree H1": { "price_usd": 90000, "power_w": 600, "battery_h": 3.5, "maintenance_pct": 0.18, "lifespan_yr": 6, "task_accuracy": 0.58, "speed_ratio": 0.45, "benchmark": "HumanoidBench (manipulation tasks)", "notes": "Research-grade; improved upper-body dexterity vs G1.", }, "Figure 02": { "price_usd": 40000, "power_w": 600, "battery_h": 4, "maintenance_pct": 0.15, "lifespan_yr": 7, "task_accuracy": 0.68, "speed_ratio": 0.60, "benchmark": "Figure internal pilots + Open X-Embodiment", "notes": "OpenAI-powered cognition; commercial warehouse focus.", }, "Agility Robotics Digit": { "price_usd": 250000, "power_w": 800, "battery_h": 8, "maintenance_pct": 0.15, "lifespan_yr": 6, "task_accuracy": 0.70, "speed_ratio": 0.70, "benchmark": "BEHAVIOR Robot Suite", "notes": "Amazon warehouse deployments; proven industrial reliability.", }, "Sanctuary AI Phoenix": { "price_usd": 65000, "power_w": 450, "battery_h": 6, "maintenance_pct": 0.12, "lifespan_yr": 7, "task_accuracy": 0.64, "speed_ratio": 0.55, "benchmark": "BEHAVIOR Robot Suite (safety-critical tasks)", "notes": "Carbon AI brain; general-purpose design; strong safety scores.", }, "Boston Dynamics Atlas (Electric)": { "price_usd": 370000, "power_w": 1500, "battery_h": 4, "maintenance_pct": 0.15, "lifespan_yr": 6, "task_accuracy": 0.72, "speed_ratio": 0.65, "benchmark": "HumanoidBench + industrial pilot programs", "notes": "Most agile humanoid; industrial & research grade; high energy draw.", }, } # ── Task categories ─────────────────────────────────────────────────────────── # accuracy_mod: scales robot's base accuracy for this task class. # speed_mod: scales robot's base speed for this task class. # >1.0 = task is more robot-friendly (repetitive/structured) # <1.0 = task requires dexterity/judgment that slows robots down TASKS = { "House Cleaning / Maid": { "accuracy_mod": 1.00, "speed_mod": 0.90, "description": "Vacuuming, mopping, dusting, laundry", }, "Cooking / Meal Prep": { "accuracy_mod": 0.70, "speed_mod": 0.55, "description": "Preparing meals, kitchen tasks — high dexterity demand", }, "Gardening / Landscaping": { "accuracy_mod": 0.80, "speed_mod": 1.05, "description": "Mowing, trimming, planting, watering — methodical, benefits robots", }, "Warehouse / Logistics": { "accuracy_mod": 0.85, "speed_mod": 1.20, "description": "Picking, packing, sorting — structured environment, robot-friendly", }, "General Maintenance": { "accuracy_mod": 0.60, "speed_mod": 0.70, "description": "Minor repairs, furniture assembly, painting", }, "Plumbing": { "accuracy_mod": 0.45, "speed_mod": 0.45, "description": "Pipe repairs, installations, leak fixes — complex manipulation", }, "Electrical Work": { "accuracy_mod": 0.40, "speed_mod": 0.45, "description": "Wiring, outlets, fixture installations — high precision required", }, "Elder / Child Care Assistance": { "accuracy_mod": 0.35, "speed_mod": 0.80, "description": "Mobility assistance, simple care tasks", }, } # ── Currencies ──────────────────────────────────────────────────────────────── CURRENCIES = { "USD 🇺🇸": {"symbol": "$", "rate": 1.000}, "EUR 🇪🇺": {"symbol": "€", "rate": 0.920}, "GBP 🇬🇧": {"symbol": "£", "rate": 0.790}, "JPY 🇯🇵": {"symbol": "¥", "rate": 154.0}, "CNY 🇨🇳": {"symbol": "¥", "rate": 7.240}, "INR 🇮🇳": {"symbol": "₹", "rate": 83.50}, "AUD 🇦🇺": {"symbol": "A$", "rate": 1.520}, "CAD 🇨🇦": {"symbol": "C$", "rate": 1.360}, "BRL 🇧🇷": {"symbol": "R$", "rate": 5.050}, "MXN 🇲🇽": {"symbol": "M$", "rate": 17.20}, "CHF 🇨🇭": {"symbol": "Fr", "rate": 0.900}, "KRW 🇰🇷": {"symbol": "₩", "rate": 1340.}, "SGD 🇸🇬": {"symbol": "S$", "rate": 1.340}, "SEK 🇸🇪": {"symbol": "kr", "rate": 10.50}, "NOK 🇳🇴": {"symbol": "kr", "rate": 10.80}, } ELEC_HINTS = { "USD 🇺🇸": 0.15, "EUR 🇪🇺": 0.28, "GBP 🇬🇧": 0.24, "JPY 🇯🇵": 21.0, "CNY 🇨🇳": 0.80, "INR 🇮🇳": 6.50, "AUD 🇦🇺": 0.30, "CAD 🇨🇦": 0.13, "BRL 🇧🇷": 0.70, "MXN 🇲🇽": 1.10, "CHF 🇨🇭": 0.22, "KRW 🇰🇷": 120., "SGD 🇸🇬": 0.28, "SEK 🇸🇪": 1.50, "NOK 🇳🇴": 1.00, } # ── Data sources reference ──────────────────────────────────────────────────── DATA_SOURCES_MD = """ ## 📚 Data Sources & Methodology ### Accuracy benchmarks | Benchmark | What it measures | Key finding | |---|---|---| | **[HumanoidBench](https://humanoid-bench.github.io/)** (He et al., 2024) | 27 whole-body tasks: 12 locomotion + 15 dexterous manipulation | Best policies reach ~40–72% success on manipulation; locomotion is more reliable | | **[BEHAVIOR Robot Suite](https://behavior.stanford.edu/behavior-1k/)** (Li et al., 2023) | 1,000 household activities in realistic home environments | Most robots fail >60% of tasks; safety-critical subset: ~64% safe-success | | **[Open X-Embodiment](https://robotics-transformer-x.github.io/)** (Padalkar et al., 2023) | Cross-robot manipulation across 22 platforms, 500k+ trajectories | RT-2-X improves generalization by ~50% vs. single-robot policies | | **[RoboSuite](https://robosuite.ai/)** (Zhu et al., 2020) | 9 manipulation tasks in MuJoCo physics simulation | Standard reproducible baseline; widely used in ablation studies | | **Manufacturer pilot reports** | Real-world warehouse/factory deployments (2024–25) | Amazon/Agility: ~70% task completion in structured environments | --- ### Speed benchmarks Speed data is **less standardized** than accuracy. The values used here are derived from: | Source | Method | |---|---| | HumanoidBench task-completion-time metrics | Ratio of median robot time vs. human baseline per task | | Figure AI public demo videos (2024) | Frame-by-frame timing of pick-and-place vs. human operator | | Agility Robotics / Amazon pilot reports | Packages-per-hour cited vs. human warehouse worker average | | Boston Dynamics Atlas demos | Time-to-complete on obstacle/manipulation sequences | **Speed ratio definition:** `1.0` = same throughput as a human worker. `0.5` = robot takes twice as long per task (delivers half the output per operating hour). --- ### How the model works ``` effective_speed = robot.base_speed × task.speed_modifier effective_accuracy = robot.base_accuracy × task.accuracy_modifier value_per_hour = effective_accuracy × effective_speed × hourly_wage value_per_year = value_per_hour × operable_hours_per_year annual_opex = energy_cost + maintenance_cost annual_net_savings = value_per_year − annual_opex break_even_years = robot_purchase_price / annual_net_savings ``` **Key insight:** a robot that is half as fast as a human (speed = 0.5) delivers only half the value per operating hour, doubling the effective cost of the task even if accuracy is identical. Energy cost is *not* speed-adjusted — the robot consumes power the whole time it runs. --- ### Accuracy modifiers by task | Task | Accuracy mod | Speed mod | Rationale | |---|---|---|---| | House Cleaning | 1.00 | 0.90 | Baseline; unstructured but tolerable | | Cooking / Meal Prep | 0.70 | 0.55 | High dexterity; fragile objects | | Gardening | 0.80 | 1.05 | Methodical; no time pressure | | Warehouse / Logistics | 0.85 | 1.20 | Structured; robots outpace humans at scale | | General Maintenance | 0.60 | 0.70 | Variable tasks; judgment required | | Plumbing | 0.45 | 0.45 | Fine manipulation; spatial reasoning | | Electrical Work | 0.40 | 0.45 | Precision-critical; safety constraints | | Elder / Child Care | 0.35 | 0.80 | Social intelligence bottleneck | --- ### Caveats & limitations - Robot prices are list/target prices as of 2025; volume discounts and leasing models are not modelled. - Accuracy and speed data are approximate; variance across environments is high. - Maintenance costs (10–20% of purchase price per year) are industry estimates; actual costs depend on usage intensity and support contracts. - Exchange rates are approximate mid-2025 values embedded at build time. - The model assumes the robot works independently; human supervision costs are not included. --- *Built by [@paolodiprodi](https://x.com/paolodiprodi) · Suggestions via the [community tab](https://huggingface.co/spaces/robomotic/robudget/discussions) or on X.* """ # ── Helpers ─────────────────────────────────────────────────────────────────── def _fmt(value: float, sym: str, decimals: int = 0) -> str: return f"{sym}{value:,.{decimals}f}" def _default_speed(robot_name: str, task_type: str) -> float: """Return the pre-computed effective speed for this robot+task pair.""" r = ROBOTS[robot_name] t = TASKS[task_type] return round(min(r["speed_ratio"] * t["speed_mod"], 1.5), 2) def update_robot_info(robot_name: str, currency_key: str) -> str: r = ROBOTS[robot_name] c = CURRENCIES[currency_key] price = r["price_usd"] * c["rate"] maint = price * r["maintenance_pct"] return ( f"**{robot_name}**\n\n" f"| Specification | Value |\n|---|---|\n" f"| Purchase price | {_fmt(price, c['symbol'])} |\n" f"| Power draw | {r['power_w']} W |\n" f"| Battery runtime | {r['battery_h']} h |\n" f"| Annual maintenance | ~{r['maintenance_pct']*100:.0f}% ≈ {_fmt(maint, c['symbol'])}/yr |\n" f"| Expected lifespan | {r['lifespan_yr']} years |\n" f"| Base task accuracy | {r['task_accuracy']*100:.0f}% |\n" f"| Base speed vs human | {r['speed_ratio']*100:.0f}% |\n" f"| Benchmark source | {r['benchmark']} |\n\n" f"*{r['notes']}*" ) def suggest_electricity(currency_key: str) -> float: return ELEC_HINTS.get(currency_key, 0.15) def prefill_speed(robot_name: str, task_type: str) -> float: return _default_speed(robot_name, task_type) # ── Core calculation ────────────────────────────────────────────────────────── def calculate( task_type: str, robot_name: str, robot_currency_key: str, wage_currency_key: str, hourly_wage: float, hours_per_day: float, days_per_week: int, electricity_cost: float, analysis_years: int, effective_speed: float, ) -> tuple: robot = ROBOTS[robot_name] task = TASKS[task_type] robot_cur = CURRENCIES[robot_currency_key] wage_cur = CURRENCIES[wage_currency_key] robot_price_robot_cur = robot["price_usd"] * robot_cur["rate"] robot_price_cmp = robot["price_usd"] * wage_cur["rate"] rsym = robot_cur["symbol"] wsym = wage_cur["symbol"] effective_accuracy = min(robot["task_accuracy"] * task["accuracy_mod"], 1.0) days_per_year = days_per_week * 52 hours_per_year = hours_per_day * days_per_year # Robot operates up to battery limit per day operable_h_per_day = min(hours_per_day, robot["battery_h"]) operable_h_per_year = operable_h_per_day * days_per_year # Energy cost: based on actual hours operated (not speed-adjusted) energy_kwh_yr = (robot["power_w"] / 1000) * operable_h_per_year energy_cost_yr = energy_kwh_yr * electricity_cost maintenance_yr = robot_price_cmp * robot["maintenance_pct"] annual_opex = energy_cost_yr + maintenance_yr # Human baseline human_annual = hourly_wage * hours_per_year # Value robot delivers: accuracy × speed × hours × wage # A robot at 0.5x speed delivers half the output per hour, even at 100% accuracy. robot_value_yr = effective_accuracy * effective_speed * operable_h_per_year * hourly_wage annual_savings = robot_value_yr - annual_opex if annual_savings <= 0: breakeven_yr = None breakeven_raw = None else: breakeven_raw = robot_price_cmp / annual_savings breakeven_yr = breakeven_raw if breakeven_raw <= robot["lifespan_yr"] else None # ── Chart ───────────────────────────────────────────────────────────────── step = 0.25 years = np.arange(0, analysis_years + step, step) human_cum = human_annual * years robot_spend_cum = robot_price_cmp + annual_opex * years robot_net_cum = robot_spend_cum - robot_value_yr * years fig = make_subplots( rows=1, cols=2, subplot_titles=( "Cumulative Cost vs. Value Delivered", f"Annual Cost Breakdown ({wsym})", ), horizontal_spacing=0.13, ) fig.add_trace(go.Scatter( x=years, y=human_cum, name="Human labor (total spend)", line=dict(color="#e74c3c", width=2.5), hovertemplate=f"Year %{{x:.2f}}
Human: {wsym}%{{y:,.0f}}", ), row=1, col=1) fig.add_trace(go.Scatter( x=years, y=robot_spend_cum, name="Robot (total spend)", line=dict(color="#3498db", width=2.5, dash="dash"), hovertemplate=f"Year %{{x:.2f}}
Robot spend: {wsym}%{{y:,.0f}}", ), row=1, col=1) fig.add_trace(go.Scatter( x=years, y=np.maximum(robot_net_cum, 0), name="Robot (net cost after value)", line=dict(color="#27ae60", width=2, dash="dot"), hovertemplate=f"Year %{{x:.2f}}
Robot net: {wsym}%{{y:,.0f}}", ), row=1, col=1) if breakeven_yr is not None: be_y = robot_price_cmp + annual_opex * breakeven_yr fig.add_vline(x=breakeven_yr, line_dash="dot", line_color="#27ae60", row=1, col=1) fig.add_annotation( x=breakeven_yr, y=be_y * 1.08, text=f"Break-even
yr {breakeven_yr:.1f}", showarrow=True, arrowhead=2, arrowcolor="#27ae60", font=dict(color="#27ae60", size=11), row=1, col=1, ) amortized = robot_price_cmp / robot["lifespan_yr"] bar_labels = ["Human\nLabor", "Robot:\nPurchase\n(amortized)", "Robot:\nEnergy", "Robot:\nMaintenance"] bar_values = [human_annual, amortized, energy_cost_yr, maintenance_yr] bar_colors = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12"] fig.add_trace(go.Bar( x=bar_labels, y=bar_values, marker_color=bar_colors, text=[_fmt(v, wsym) for v in bar_values], textposition="outside", hovertemplate="%{x}
Annual: " + wsym + "%{y:,.0f}", showlegend=False, ), row=1, col=2) fig.update_layout( height=500, template="plotly_white", showlegend=True, legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="left", x=0), margin=dict(t=70, b=50, l=50, r=20), ) fig.update_yaxes(tickprefix=wsym, row=1, col=1) fig.update_yaxes(tickprefix=wsym, row=1, col=2) fig.update_xaxes(title_text="Years", row=1, col=1) # ── Summary ──────────────────────────────────────────────────────────────── net_gain = robot_value_yr * robot["lifespan_yr"] - (robot_price_cmp + annual_opex * robot["lifespan_yr"]) if breakeven_yr is None: if annual_savings <= 0: be_block = ( "### ❌ No Break-even — robot costs exceed value delivered\n\n" f"Annual net savings: **{_fmt(annual_savings, wsym)}**. " "Try a higher wage, more hours, or a cheaper/faster robot." ) else: be_block = ( f"### ⚠️ Break-even ({breakeven_raw:.1f} yr) exceeds lifespan ({robot['lifespan_yr']} yr)\n\n" "The robot would eventually pay off, but not within its operational life." ) else: be_block = ( f"### ✅ Break-even at **{breakeven_yr:.1f} years**\n\n" f"Annual net savings: **{_fmt(annual_savings, wsym)}/yr**\n\n" f"Net gain over full {robot['lifespan_yr']}-yr lifespan: **{_fmt(net_gain, wsym)}**" ) currency_note = "" if robot_currency_key != wage_currency_key: currency_note = ( f"\n> Robot price in **{rsym}**: {_fmt(robot_price_robot_cur, rsym)}. " f"Chart values converted to **{wsym}** (approx. rates)." ) default_spd = _default_speed(robot_name, task_type) speed_note = "" if abs(effective_speed - default_spd) > 0.01: speed_note = f" *(manually overridden from benchmark default {default_spd:.2f})*" summary = f""" ## {robot_name} → {task_type} | Parameter | Value | |---|---| | Robot price | {_fmt(robot_price_robot_cur, rsym)} | | Effective accuracy | {effective_accuracy*100:.1f}% *(base {robot['task_accuracy']*100:.0f}% × {task['accuracy_mod']*100:.0f}% task modifier)* | | Effective speed vs human | {effective_speed*100:.0f}%{speed_note} | | Operable hours/day | {operable_h_per_day:.1f} h *(battery capped from {hours_per_day:.1f} h)* | | Annual energy cost | {_fmt(energy_cost_yr, wsym)} | | Annual maintenance | {_fmt(maintenance_yr, wsym)} | | Annual human labor | {_fmt(human_annual, wsym)} | | Robot value delivered/yr | {_fmt(robot_value_yr, wsym)} *(accuracy × speed × hours × wage)* | | **Annual net savings** | **{_fmt(annual_savings, wsym)}** | --- {be_block} --- **Benchmark:** {robot['benchmark']} **Robot note:** {robot['notes']} **Task note:** {task['description']}{currency_note} *Exchange rates approximate (mid-2025). See the Data Sources tab for full methodology.* """ return fig, summary # ── UI ──────────────────────────────────────────────────────────────────────── with gr.Blocks(title="RoBudget — Humanoid Robot Investment Calculator") as demo: gr.Markdown(""" # 🤖 RoBudget — Should You Hire a Humanoid Robot? Break-even analysis comparing the total cost of **human labor** vs. **buying and operating a humanoid robot**. """) with gr.Tabs(): # ── Tab 1: Calculator ───────────────────────────────────────────────── with gr.Tab("🧮 Calculator"): with gr.Row(): # Left: inputs with gr.Column(scale=1, min_width=330): gr.Markdown("### 🌍 Currency") with gr.Row(): robot_currency = gr.Dropdown( choices=list(CURRENCIES.keys()), value="USD 🇺🇸", label="Robot price currency", scale=1, ) wage_currency = gr.Dropdown( choices=list(CURRENCIES.keys()), value="USD 🇺🇸", label="Wages & electricity currency", scale=1, ) gr.Markdown("### 🔧 Task & Robot") task_type = gr.Dropdown( choices=list(TASKS.keys()), value="House Cleaning / Maid", label="Task / Job type", info="The chore or role the robot would replace", ) robot_name = gr.Dropdown( choices=list(ROBOTS.keys()), value="Tesla Optimus Gen 2", label="Robot model", ) gr.Markdown("### 💰 Labor Cost") hourly_wage = gr.Slider( minimum=1, maximum=500, value=20, step=1, label="Human hourly wage (wage currency)", ) hours_per_day = gr.Slider( minimum=0.5, maximum=12, value=4, step=0.5, label="Hours of work per day", ) days_per_week = gr.Slider( minimum=1, maximum=7, value=5, step=1, label="Days worked per week", ) gr.Markdown("### ⚡ Energy") electricity_cost = gr.Slider( minimum=0.01, maximum=500, value=0.15, step=0.01, label="Electricity cost per kWh (wage currency)", info="US ≈ $0.15 · EU ≈ €0.28 · UK ≈ £0.24 · JP ≈ ¥21", ) gr.Markdown("### 🏎️ Robot Performance") effective_speed = gr.Slider( minimum=0.10, maximum=1.50, value=0.50, step=0.05, label="Effective speed vs. human (auto-filled from benchmarks)", info="1.0 = same speed as human · 0.5 = twice as slow · >1.0 = faster", ) gr.Markdown( "*Pre-filled from benchmark data for the selected robot + task. " "Override to model future improvements or your own observations.*" ) gr.Markdown("### 📅 Analysis Horizon") analysis_years = gr.Slider( minimum=1, maximum=20, value=10, step=1, label="Years to project", ) calc_btn = gr.Button("⚡ Calculate Break-even", variant="primary") # Right: outputs with gr.Column(scale=2): gr.Markdown("### 📊 Results") output_chart = gr.Plot(label="Cost Analysis") output_summary = gr.Markdown( "*Configure inputs and press **Calculate Break-even** to see results.*" ) with gr.Accordion("ℹ️ Selected Robot Specifications", open=False): robot_spec = gr.Markdown() # ── Tab 2: Data Sources ─────────────────────────────────────────────── with gr.Tab("📚 Data Sources & Methodology"): gr.Markdown(DATA_SOURCES_MD) gr.Markdown(""" --- Built by **[@paolodiprodi](https://x.com/paolodiprodi)** · Suggestions welcome — open a discussion on the [community tab](https://huggingface.co/spaces/robomotic/robudget/discussions) or reach out on X. """) # ── Wiring ──────────────────────────────────────────────────────────────── def on_robot_or_task_change(robot_name, task_type, robot_cur, wage_cur): spec = update_robot_info(robot_name, robot_cur) spd = prefill_speed(robot_name, task_type) elec = suggest_electricity(wage_cur) return spec, spd, elec for trigger in [robot_name, task_type, robot_currency, wage_currency]: trigger.change( on_robot_or_task_change, inputs=[robot_name, task_type, robot_currency, wage_currency], outputs=[robot_spec, effective_speed, electricity_cost], ) calc_btn.click( calculate, inputs=[ task_type, robot_name, robot_currency, wage_currency, hourly_wage, hours_per_day, days_per_week, electricity_cost, analysis_years, effective_speed, ], outputs=[output_chart, output_summary], ) demo.launch(theme=gr.themes.Soft())