Metadata-Version: 2.4
Name: roboinfra-sdk
Version: 1.0.12
Summary: Python SDK for RoboInfra URDF validation, kinematic analysis, semantic diff, SDF/MJCF conversion, MoveIt config generation, 3D model conversion and mesh analysis APIs for ROS developers
Home-page: https://github.com/Ravindar10/roboinfra-python-sdk
Author: RoboInfra
Keywords: robotics,ros,urdf,validation,robot,kinematic,3d-model,mesh-analysis,gazebo,ci-cd,urdf-validator,model-conversion,stl,fbx,obj,gltf,moveit
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.20.0
Dynamic: author
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# roboinfra-sdk

Python SDK for RoboInfra Robotics API for URDF validation, kinematic analysis, 3D model conversion and mesh quality analysis.

[![PyPI version](https://badge.fury.io/py/roboinfra-sdk.svg)](https://pypi.org/project/roboinfra-sdk/)
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/)

**API:** `https://roboinfra-api.azurewebsites.net`  
**Dashboard:** `https://roboinfra-dashboard.azurewebsites.net`  
**PyPI:** `https://pypi.org/project/roboinfra-sdk/`  
**GitHub Action:** `uses: roboinfra/validate-urdf-action@v1`

---

## Installation

```bash
pip install roboinfra-sdk
```

---

## Quick Start

```python
import roboinfra as roboinfra

# Create client with your API key from the dashboard
client = roboinfra.Client("rk_your_api_key_here")

# 1. Check API health (no auth needed)
status = client.health()
print(status["status"])          # "Healthy"

# 2. Validate a URDF file (Free plan)
result = client.urdf.validate("robot.urdf")
if result.is_valid:
    print("URDF is valid!")
else:
    for error in result.errors:
        print(f"  Error: {error}")

# 3. Kinematic analysis (Basic + Pro plan)
analysis = client.urdf.analyze("robot.urdf")
print(f"DOF: {analysis.dof}")
print(f"End effectors: {analysis.end_effectors}")

# 4. Convert 3D model (Pro plan)
output = client.model.convert("robot.obj", "fbx", "robot.fbx")
print(f"Saved: {output}")

# 5. Mesh quality analysis (Pro plan)
mesh = client.model.analyze("robot.stl")
print(f"Triangles: {mesh.total_triangles}, Watertight: {mesh.is_watertight}")

# 6. URDF â†’ SDF/MJCF conversion (Pro plan)
sdf = client.urdf.convert_format("robot.urdf", "sdf")
with open("robot.sdf", "w") as f:
    f.write(sdf.converted_xml)
print(f"Converted to SDF: {sdf.link_count} links, {sdf.joint_count} joints")

mjcf = client.urdf.convert_format("robot.urdf", "mjcf")
with open("robot.xml", "w") as f:
    f.write(mjcf.converted_xml)
print(f"Converted to MJCF: {mjcf.link_count} links, {mjcf.joint_count} joints")

# 7. MoveIt config generation (Pro plan)
cfg = client.urdf.moveit_config("robot.urdf")
with open("config/kinematics.yaml", "w") as f:
    f.write(cfg.kinematics_yaml)
with open(f"config/{cfg.robot_name}.srdf", "w") as f:
    f.write(cfg.srdf)
print(f"MoveIt: {len(cfg.planning_groups)} groups, "
      f"{cfg.movable_joint_count} movable joints")
```

---

## Get an API Key

1. Register at [roboinfra-dashboard.azurewebsites.net](https://roboinfra-dashboard.azurewebsites.net)
2. Go to **API Keys** â†’ Create key
3. Keys start with `rk_`
4. Free plan: 50 calls/month for URDF validation  no credit card required

---

## API Reference

### `client.health()`  Public, no key needed

Check API connectivity and service status.

```python
result = client.health()
# Returns dict:
# {
#   "status": "Healthy",
#   "version": "1.0.0",
#   "uptime": "2d 4h 12m"
# }
```

---

### `client.urdf.validate(file_path)`  All plans (Free/Basic/Pro)

Validates a URDF file against 9 structural checks.

```python
result = client.urdf.validate("robot.urdf")

result.is_valid   # bool   True if all 9 checks pass
result.errors     # list   empty when valid, list of strings when invalid
```

**9 checks performed:**
1. Root element must be `<robot>`
2. At least one `<link>` must exist
3. No duplicate link names
4. No duplicate joint names
5. All joint `parent` links must reference a defined link
6. All joint `child` links must reference a defined link
7. Joint `type` must be valid (revolute, continuous, prismatic, fixed, floating, planar)
8. `revolute` and `prismatic` joints must include `<limit>`
9. Exactly one root link (no cycles, no orphans)

**Valid URDF example:**
```python
result = client.urdf.validate("robot.urdf")
# result.is_valid â†’ True
# result.errors   â†’ []
```

**Invalid URDF example:**
```python
result = client.urdf.validate("bad_robot.urdf")
# result.is_valid â†’ False
# result.errors   â†’ [
#     "Root element must be <robot>",
#     "At least one <link> must exist"
# ]
```

---

### `client.urdf.analyze(file_path)`  Basic + Pro plan

Kinematic analysis  DOF, joint chain, end effectors.

```python
result = client.urdf.analyze("robot.urdf")

result.robot_name      # str    name from <robot name="...">
result.link_count      # int    total links
result.joint_count     # int    total joints
result.dof             # int    degrees of freedom (non-fixed joints)
result.max_chain_depth # int    longest kinematic chain
result.root_link       # str    root link name
result.end_effectors   # list   end effector link names
result.joints          # list   joint details
```

**Example:**
```python
result = client.urdf.analyze("robot.urdf")
# result.robot_name      â†’ "sample_arm"
# result.dof             â†’ 2
# result.end_effectors   â†’ ["tool0"]
# result.joints[0]       â†’ {"name": "joint_1", "type": "revolute",
#                            "parent": "base_link", "child": "link_1"}
```

---


---

### `client.urdf.convert_format(file_path, target_format)`  Pro plan

Convert URDF to Gazebo SDF or MuJoCo MJCF format.

```python
# Convert to Gazebo SDF
sdf = client.urdf.convert_format("robot.urdf", "sdf")
with open("robot.sdf", "w") as f:
    f.write(sdf.converted_xml)

# Convert to MuJoCo MJCF
mjcf = client.urdf.convert_format("robot.urdf", "mjcf")
with open("robot.xml", "w") as f:
    f.write(mjcf.converted_xml)

# Check warnings (e.g. mesh file references)
for w in mjcf.warnings:
    print(f"  Warning: {w}")

sdf.target_format    # str  â€” "sdf" or "mjcf"
sdf.robot_name       # str  â€” robot name from URDF
sdf.converted_xml    # str  â€” the output XML
sdf.link_count       # int
sdf.joint_count      # int
sdf.warnings         # list â€” non-fatal notes
```

**Supported formats:** `"sdf"` (Gazebo Sim v1.7) and `"mjcf"` (MuJoCo / Google DeepMind).

**What gets converted:** all 6 joint types, inertia tensors, visual + collision geometry, materials, mesh references, joint limits/damping/friction, origin transforms. MJCF includes auto-generated actuators.

### `client.urdf.moveit_config(file_path)`  Pro plan

Generate MoveIt configuration starter files from a URDF â€” `kinematics.yaml`, `joint_limits.yaml`, an SRDF stub, and planning group suggestions. A faster starting point than the MoveIt Setup Assistant GUI (seconds instead of hours).

```python
cfg = client.urdf.moveit_config("robot.urdf")

# Save the generated files into a moveit_config package
with open("config/kinematics.yaml", "w") as f:
    f.write(cfg.kinematics_yaml)
with open("config/joint_limits.yaml", "w") as f:
    f.write(cfg.joint_limits_yaml)
with open(f"config/{cfg.robot_name}.srdf", "w") as f:
    f.write(cfg.srdf)

# Inspect detected planning groups
for g in cfg.planning_groups:
    print(f"{g['name']}: {len(g['joints'])} joints "
          f"({g.get('baseLink','')} -> {g.get('tipLink','')})")

cfg.robot_name           # str
cfg.kinematics_yaml      # str  â€” config/kinematics.yaml (KDL solver)
cfg.joint_limits_yaml    # str  â€” config/joint_limits.yaml
cfg.srdf                 # str  â€” config/<robot>.srdf
cfg.planning_groups      # list â€” [{name, joints, baseLink, tipLink, reason}]
cfg.movable_joint_count  # int
cfg.base_link            # str
cfg.tip_link             # str
cfg.warnings             # list
```

**What it generates:** KDL kinematics config per planning group, velocity limits read from the URDF (with sensible acceleration defaults), an SRDF stub with planning groups / virtual joint / home state / adjacent-link `disable_collisions`, and auto-detected planning groups (arm chain + gripper separated by joint name).

This is a **starter**, not a full replacement for the MoveIt Setup Assistant. Review the SRDF collision rules and tune acceleration limits for your hardware before real execution.

### `client.model.convert(file_path, target_format, output_path)`  Pro plan

Convert 3D model files between formats. No Blender required.

```python
output = client.model.convert("robot.stl", "obj", "robot.obj")
# Returns absolute path to saved output file

# All supported conversions:
# .fbx  â†’ obj, stl, glb, gltf
# .obj  â†’ stl, glb, gltf, fbx
# .stl  â†’ obj, glb, gltf
# .gltf â†’ obj, stl, glb
# .glb  â†’ obj, stl, gltf
# .dae  â†’ obj, stl, glb
# .3ds  â†’ obj, stl, glb
# .blendâ†’ obj, stl, glb
```

**Examples:**
```python
client.model.convert("robot.stl", "obj",  "robot.obj")
client.model.convert("robot.obj", "fbx",  "robot.fbx")
client.model.convert("robot.fbx", "glb",  "robot.glb")
client.model.convert("robot.obj", "gltf", "robot.gltf")
```

---

### `client.model.analyze(file_path)`  Pro plan

Mesh quality analysis for physics simulation readiness.

```python
result = client.model.analyze("robot.stl")

result.mesh_count       # int    number of mesh objects
result.total_vertices   # int    total vertex count
result.total_triangles  # int    triangle count after triangulation
result.material_count   # int    number of materials
result.has_bones        # bool   True if skeletal data detected
result.is_watertight    # bool   True if ALL meshes are watertight (required for physics)
result.bounding_box     # dict   {"x": 0.42, "y": 0.38, "z": 0.75}
result.center_of_mass   # dict   {"x": 0.0, "y": 0.0, "z": 0.21}
result.meshes           # list   per-mesh breakdown
```

**Example:**
```python
result = client.model.analyze("robot_arm.stl")
# result.total_triangles â†’ 30240
# result.is_watertight   â†’ True
# result.bounding_box    â†’ {"x": 0.42, "y": 0.38, "z": 0.75}

if not result.is_watertight:
    print("WARNING: mesh has holes  robot will fall through ground in Gazebo!")
```

---

## Error Handling

```python
from roboinfra import (
    RoboInfraError,   # base  any API error
    AuthError,        # 401  invalid API key
    PlanError,        # 403  endpoint requires higher plan
    QuotaError,       # 429  monthly quota exceeded
)

try:
    result = client.urdf.validate("robot.urdf")
except AuthError:
    print("Invalid API key  get one at roboinfra-dashboard.azurewebsites.net/keys")
except PlanError:
    print("Upgrade your plan at roboinfra-dashboard.azurewebsites.net/subscription")
except QuotaError:
    print("Monthly quota exceeded  upgrade or wait until next month")
except RoboInfraError as e:
    print(f"API error: {e} (HTTP {e.status_code})")
```

> **Note:** 400 errors (bad file, oversized file) also count against your monthly quota.
> The SDK validates file size and extension **locally before any HTTP call** to avoid
> wasting quota on obvious errors.

---

## Plans and Limits

| Plan | Price | Quota | Features |
|------|-------|-------|---------|
| Free | $0/month | 50 calls | URDF validation only |
| Basic | $25/month | 500 calls | URDF validation + kinematic analysis |
| Pro | $75/month | 5,000 calls | All features (3D conversion + mesh analysis) |

---

## File Limits

| Endpoint | Max file size | Allowed extensions |
|----------|--------------|-------------------|
| `urdf.validate` | **1 MB** | `.urdf` |
| `urdf.analyze` | **1 MB** | `.urdf` |
| `model.convert` | **20 MB** | `.fbx .obj .stl .gltf .glb .dae .3ds .blend` |
| `model.analyze` | **20 MB** | `.fbx .obj .stl .gltf .glb .dae .3ds .blend` |

The SDK validates file size and extension **locally before any HTTP call**  no wasted quota on obvious errors.

---

## CI/CD Integration

### Option 1  GitHub Action (simplest, recommended)

No Python required. Just add 3 lines to your workflow:

```yaml
# .github/workflows/validate-urdf.yml
name: Validate URDF
on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: roboinfra/validate-urdf-action@v1
        with:
          api-key: ${{ secrets.ROBOINFRA_API_KEY }}
          file: urdf/robot.urdf
```

Full docs: [github.com/roboinfra/validate-urdf-action](https://github.com/Ravindar10/validate-urdf-action)

---

### Option 2  SDK inside a run step (more control)

Use this approach when you need custom logic  multiple files, conditional checks, or integration with other tools:

```yaml
- name: Validate URDF
  run: |
    pip install roboinfra-sdk
    python - <<'EOF'
    import roboinfra as roboinfra
    import sys
    client = roboinfra.Client("${{ secrets.ROBOINFRA_API_KEY }}")
    result = client.urdf.validate("urdf/robot.urdf")
    if not result.is_valid:
        print("URDF validation failed:")
        for e in result.errors:
            print(f"  - {e}")
        sys.exit(1)
    print("URDF valid!")
    EOF
```

---

## License

MIT
