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())