Spaces:
No application file
No application file
sam133
commited on
Commit
Β·
833d580
1
Parent(s):
e35c229
Remove: Delete mcp and cursor_pybullet_mcp implementation
Browse files- mcp/cursor_pybullet/README.md +0 -103
- mcp/cursor_pybullet/__init__.py +0 -4
- mcp/cursor_pybullet/config.json +0 -176
- mcp/cursor_pybullet/mcp.py +0 -248
- mcp/cursor_pybullet/record_simulation.py +0 -105
- mcp/cursor_pybullet/setup.py +0 -25
mcp/cursor_pybullet/README.md
DELETED
|
@@ -1,103 +0,0 @@
|
|
| 1 |
-
# Cursor-PyBullet MCP
|
| 2 |
-
|
| 3 |
-
A Model-Controller-Provider (MCP) for connecting Cursor with PyBullet physics simulation.
|
| 4 |
-
|
| 5 |
-
## Features
|
| 6 |
-
|
| 7 |
-
- Connect to PyBullet physics server
|
| 8 |
-
- Create and control robots
|
| 9 |
-
- Run physics simulations
|
| 10 |
-
- Capture visualization frames
|
| 11 |
-
- Monitor robot state
|
| 12 |
-
- Apply control signals
|
| 13 |
-
|
| 14 |
-
## Installation
|
| 15 |
-
|
| 16 |
-
```bash
|
| 17 |
-
pip install -e .
|
| 18 |
-
```
|
| 19 |
-
|
| 20 |
-
## Usage
|
| 21 |
-
|
| 22 |
-
```python
|
| 23 |
-
from cursor_pybullet_mcp import CursorPyBulletMCP
|
| 24 |
-
|
| 25 |
-
# Create MCP instance
|
| 26 |
-
mcp = CursorPyBulletMCP()
|
| 27 |
-
|
| 28 |
-
# Connect to PyBullet
|
| 29 |
-
if mcp.connect():
|
| 30 |
-
try:
|
| 31 |
-
# Create ground
|
| 32 |
-
mcp.create_ground()
|
| 33 |
-
|
| 34 |
-
# Create robot
|
| 35 |
-
robot_specs = {
|
| 36 |
-
"body": {
|
| 37 |
-
"dimensions": [1.0, 0.5, 0.3],
|
| 38 |
-
"mass": 10.0
|
| 39 |
-
},
|
| 40 |
-
"wheels": [
|
| 41 |
-
{
|
| 42 |
-
"position": [0.5, 0.3, 0.15],
|
| 43 |
-
"radius": 0.1,
|
| 44 |
-
"width": 0.05
|
| 45 |
-
},
|
| 46 |
-
{
|
| 47 |
-
"position": [0.5, -0.3, 0.15],
|
| 48 |
-
"radius": 0.1,
|
| 49 |
-
"width": 0.05
|
| 50 |
-
},
|
| 51 |
-
{
|
| 52 |
-
"position": [-0.5, 0.3, 0.15],
|
| 53 |
-
"radius": 0.1,
|
| 54 |
-
"width": 0.05
|
| 55 |
-
},
|
| 56 |
-
{
|
| 57 |
-
"position": [-0.5, -0.3, 0.15],
|
| 58 |
-
"radius": 0.1,
|
| 59 |
-
"width": 0.05
|
| 60 |
-
}
|
| 61 |
-
]
|
| 62 |
-
}
|
| 63 |
-
mcp.create_robot(robot_specs)
|
| 64 |
-
|
| 65 |
-
# Setup camera
|
| 66 |
-
mcp.setup_camera()
|
| 67 |
-
|
| 68 |
-
# Run simulation
|
| 69 |
-
frames = mcp.run_simulation(duration_sec=5)
|
| 70 |
-
print(f"Captured {len(frames)} frames")
|
| 71 |
-
|
| 72 |
-
finally:
|
| 73 |
-
# Always disconnect
|
| 74 |
-
mcp.disconnect()
|
| 75 |
-
```
|
| 76 |
-
|
| 77 |
-
## Configuration
|
| 78 |
-
|
| 79 |
-
The MCP can be configured using a JSON file:
|
| 80 |
-
|
| 81 |
-
```json
|
| 82 |
-
{
|
| 83 |
-
"physics": {
|
| 84 |
-
"gravity": [0, 0, -9.81],
|
| 85 |
-
"timestep": 1.0/240.0,
|
| 86 |
-
"max_steps": 1000
|
| 87 |
-
},
|
| 88 |
-
"camera": {
|
| 89 |
-
"width": 640,
|
| 90 |
-
"height": 480,
|
| 91 |
-
"fov": 60,
|
| 92 |
-
"near": 0.1,
|
| 93 |
-
"far": 100.0,
|
| 94 |
-
"position": [2, 2, 2],
|
| 95 |
-
"target": [0, 0, 0],
|
| 96 |
-
"up": [0, 0, 1]
|
| 97 |
-
}
|
| 98 |
-
}
|
| 99 |
-
```
|
| 100 |
-
|
| 101 |
-
## License
|
| 102 |
-
|
| 103 |
-
MIT License
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/cursor_pybullet/__init__.py
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
from .mcp import CursorPyBulletMCP
|
| 2 |
-
|
| 3 |
-
__version__ = "1.0.0"
|
| 4 |
-
__all__ = ["CursorPyBulletMCP"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/cursor_pybullet/config.json
DELETED
|
@@ -1,176 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "Cursor-PyBullet MCP",
|
| 3 |
-
"version": "1.0.0",
|
| 4 |
-
"description": "Model-Controller-Provider for connecting Cursor with PyBullet physics simulation",
|
| 5 |
-
"type": "python",
|
| 6 |
-
"entry_point": "cursor_pybullet_mcp.py",
|
| 7 |
-
"dependencies": {
|
| 8 |
-
"pybullet": "3.2.5",
|
| 9 |
-
"numpy": "1.24.3",
|
| 10 |
-
"Pillow": "10.0.0"
|
| 11 |
-
},
|
| 12 |
-
"config": {
|
| 13 |
-
"physics": {
|
| 14 |
-
"gravity": [0, 0, -9.81],
|
| 15 |
-
"timestep": 1.0/240.0,
|
| 16 |
-
"max_steps": 1000
|
| 17 |
-
},
|
| 18 |
-
"camera": {
|
| 19 |
-
"width": 640,
|
| 20 |
-
"height": 480,
|
| 21 |
-
"fov": 60,
|
| 22 |
-
"near": 0.1,
|
| 23 |
-
"far": 100.0,
|
| 24 |
-
"position": [2, 2, 2],
|
| 25 |
-
"target": [0, 0, 0],
|
| 26 |
-
"up": [0, 0, 1]
|
| 27 |
-
},
|
| 28 |
-
"robot": {
|
| 29 |
-
"default_body": {
|
| 30 |
-
"dimensions": [1.0, 0.5, 0.3],
|
| 31 |
-
"mass": 10.0,
|
| 32 |
-
"color": [0.8, 0.2, 0.2, 1.0]
|
| 33 |
-
},
|
| 34 |
-
"default_wheel": {
|
| 35 |
-
"radius": 0.1,
|
| 36 |
-
"width": 0.05,
|
| 37 |
-
"mass": 1.0,
|
| 38 |
-
"color": [0.2, 0.2, 0.2, 1.0]
|
| 39 |
-
}
|
| 40 |
-
},
|
| 41 |
-
"simulation": {
|
| 42 |
-
"capture_interval": 100,
|
| 43 |
-
"default_duration": 7
|
| 44 |
-
}
|
| 45 |
-
},
|
| 46 |
-
"commands": {
|
| 47 |
-
"connect": {
|
| 48 |
-
"description": "Connect to PyBullet physics server",
|
| 49 |
-
"returns": "boolean"
|
| 50 |
-
},
|
| 51 |
-
"disconnect": {
|
| 52 |
-
"description": "Disconnect from PyBullet physics server",
|
| 53 |
-
"returns": "void"
|
| 54 |
-
},
|
| 55 |
-
"create_ground": {
|
| 56 |
-
"description": "Create a ground plane",
|
| 57 |
-
"returns": "boolean"
|
| 58 |
-
},
|
| 59 |
-
"create_robot": {
|
| 60 |
-
"description": "Create a robot from specifications",
|
| 61 |
-
"parameters": {
|
| 62 |
-
"body": {
|
| 63 |
-
"dimensions": "array[3]",
|
| 64 |
-
"mass": "float"
|
| 65 |
-
},
|
| 66 |
-
"wheels": "array of wheel specifications"
|
| 67 |
-
},
|
| 68 |
-
"returns": "boolean"
|
| 69 |
-
},
|
| 70 |
-
"run_simulation": {
|
| 71 |
-
"description": "Run the simulation and capture frames",
|
| 72 |
-
"parameters": {
|
| 73 |
-
"duration_sec": "float",
|
| 74 |
-
"capture_interval": "integer"
|
| 75 |
-
},
|
| 76 |
-
"returns": "array of base64 encoded frames"
|
| 77 |
-
},
|
| 78 |
-
"get_robot_state": {
|
| 79 |
-
"description": "Get current state of the robot",
|
| 80 |
-
"returns": {
|
| 81 |
-
"position": "array[3]",
|
| 82 |
-
"orientation": "array[4]",
|
| 83 |
-
"linear_velocity": "array[3]",
|
| 84 |
-
"angular_velocity": "array[3]"
|
| 85 |
-
}
|
| 86 |
-
},
|
| 87 |
-
"apply_control": {
|
| 88 |
-
"description": "Apply control signals to the robot",
|
| 89 |
-
"parameters": {
|
| 90 |
-
"wheel_velocities": "array of float"
|
| 91 |
-
},
|
| 92 |
-
"returns": "boolean"
|
| 93 |
-
}
|
| 94 |
-
},
|
| 95 |
-
"events": {
|
| 96 |
-
"simulation_started": {
|
| 97 |
-
"description": "Emitted when simulation starts",
|
| 98 |
-
"data": {
|
| 99 |
-
"timestamp": "string",
|
| 100 |
-
"config": "object"
|
| 101 |
-
}
|
| 102 |
-
},
|
| 103 |
-
"simulation_step": {
|
| 104 |
-
"description": "Emitted on each simulation step",
|
| 105 |
-
"data": {
|
| 106 |
-
"step": "integer",
|
| 107 |
-
"total_steps": "integer",
|
| 108 |
-
"robot_state": "object"
|
| 109 |
-
}
|
| 110 |
-
},
|
| 111 |
-
"frame_captured": {
|
| 112 |
-
"description": "Emitted when a frame is captured",
|
| 113 |
-
"data": {
|
| 114 |
-
"step": "integer",
|
| 115 |
-
"frame": "string (base64)"
|
| 116 |
-
}
|
| 117 |
-
},
|
| 118 |
-
"simulation_ended": {
|
| 119 |
-
"description": "Emitted when simulation ends",
|
| 120 |
-
"data": {
|
| 121 |
-
"timestamp": "string",
|
| 122 |
-
"total_frames": "integer",
|
| 123 |
-
"final_state": "object"
|
| 124 |
-
}
|
| 125 |
-
},
|
| 126 |
-
"error": {
|
| 127 |
-
"description": "Emitted when an error occurs",
|
| 128 |
-
"data": {
|
| 129 |
-
"error": "string",
|
| 130 |
-
"timestamp": "string",
|
| 131 |
-
"context": "object"
|
| 132 |
-
}
|
| 133 |
-
}
|
| 134 |
-
},
|
| 135 |
-
"examples": {
|
| 136 |
-
"basic_usage": {
|
| 137 |
-
"description": "Basic example of using the MCP",
|
| 138 |
-
"code": {
|
| 139 |
-
"connect": true,
|
| 140 |
-
"create_ground": true,
|
| 141 |
-
"create_robot": {
|
| 142 |
-
"body": {
|
| 143 |
-
"dimensions": [1.0, 0.5, 0.3],
|
| 144 |
-
"mass": 10.0
|
| 145 |
-
},
|
| 146 |
-
"wheels": [
|
| 147 |
-
{
|
| 148 |
-
"position": [0.5, 0.3, 0.15],
|
| 149 |
-
"radius": 0.1,
|
| 150 |
-
"width": 0.05
|
| 151 |
-
},
|
| 152 |
-
{
|
| 153 |
-
"position": [0.5, -0.3, 0.15],
|
| 154 |
-
"radius": 0.1,
|
| 155 |
-
"width": 0.05
|
| 156 |
-
},
|
| 157 |
-
{
|
| 158 |
-
"position": [-0.5, 0.3, 0.15],
|
| 159 |
-
"radius": 0.1,
|
| 160 |
-
"width": 0.05
|
| 161 |
-
},
|
| 162 |
-
{
|
| 163 |
-
"position": [-0.5, -0.3, 0.15],
|
| 164 |
-
"radius": 0.1,
|
| 165 |
-
"width": 0.05
|
| 166 |
-
}
|
| 167 |
-
]
|
| 168 |
-
},
|
| 169 |
-
"run_simulation": {
|
| 170 |
-
"duration_sec": 5,
|
| 171 |
-
"capture_interval": 100
|
| 172 |
-
}
|
| 173 |
-
}
|
| 174 |
-
}
|
| 175 |
-
}
|
| 176 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/cursor_pybullet/mcp.py
DELETED
|
@@ -1,248 +0,0 @@
|
|
| 1 |
-
import pybullet as p
|
| 2 |
-
import pybullet_data
|
| 3 |
-
import numpy as np
|
| 4 |
-
import json
|
| 5 |
-
import base64
|
| 6 |
-
import io
|
| 7 |
-
from PIL import Image
|
| 8 |
-
import time
|
| 9 |
-
import os
|
| 10 |
-
from typing import Dict, List, Optional, Union, Any
|
| 11 |
-
|
| 12 |
-
class CursorPyBulletMCP:
|
| 13 |
-
"""Cursor-PyBullet MCP implementation"""
|
| 14 |
-
|
| 15 |
-
def __init__(self, config_path: Optional[str] = None):
|
| 16 |
-
"""Initialize the MCP with optional config file"""
|
| 17 |
-
self.physics_client = None
|
| 18 |
-
self.robot_id = None
|
| 19 |
-
self.wheel_ids = []
|
| 20 |
-
self.joint_ids = []
|
| 21 |
-
self.config = self._load_config(config_path)
|
| 22 |
-
self.camera_setup = self.config.get("camera", {
|
| 23 |
-
'width': 640,
|
| 24 |
-
'height': 480,
|
| 25 |
-
'fov': 60,
|
| 26 |
-
'near': 0.1,
|
| 27 |
-
'far': 100.0
|
| 28 |
-
})
|
| 29 |
-
|
| 30 |
-
def _load_config(self, config_path: Optional[str]) -> Dict:
|
| 31 |
-
"""Load configuration from file or use defaults"""
|
| 32 |
-
if config_path and os.path.exists(config_path):
|
| 33 |
-
with open(config_path, 'r') as f:
|
| 34 |
-
return json.load(f)
|
| 35 |
-
return {
|
| 36 |
-
"physics": {
|
| 37 |
-
"gravity": [0, 0, -9.81],
|
| 38 |
-
"timestep": 1.0/240.0,
|
| 39 |
-
"max_steps": 1000
|
| 40 |
-
},
|
| 41 |
-
"camera": {
|
| 42 |
-
"width": 640,
|
| 43 |
-
"height": 480,
|
| 44 |
-
"fov": 60,
|
| 45 |
-
"near": 0.1,
|
| 46 |
-
"far": 100.0,
|
| 47 |
-
"position": [2, 2, 2],
|
| 48 |
-
"target": [0, 0, 0],
|
| 49 |
-
"up": [0, 0, 1]
|
| 50 |
-
}
|
| 51 |
-
}
|
| 52 |
-
|
| 53 |
-
def connect(self) -> bool:
|
| 54 |
-
"""Connect to PyBullet physics server"""
|
| 55 |
-
try:
|
| 56 |
-
self.physics_client = p.connect(p.DIRECT)
|
| 57 |
-
p.setAdditionalSearchPath(pybullet_data.getDataPath())
|
| 58 |
-
p.setGravity(*self.config["physics"]["gravity"])
|
| 59 |
-
p.setRealTimeSimulation(0)
|
| 60 |
-
print("β
Connected to PyBullet physics server")
|
| 61 |
-
return True
|
| 62 |
-
except Exception as e:
|
| 63 |
-
print(f"β Failed to connect to PyBullet: {str(e)}")
|
| 64 |
-
return False
|
| 65 |
-
|
| 66 |
-
def disconnect(self) -> None:
|
| 67 |
-
"""Disconnect from PyBullet physics server"""
|
| 68 |
-
if self.physics_client is not None:
|
| 69 |
-
p.disconnect(self.physics_client)
|
| 70 |
-
print("β
Disconnected from PyBullet physics server")
|
| 71 |
-
|
| 72 |
-
def create_ground(self) -> bool:
|
| 73 |
-
"""Create a ground plane"""
|
| 74 |
-
try:
|
| 75 |
-
ground_id = p.createCollisionShape(p.GEOM_PLANE)
|
| 76 |
-
p.createMultiBody(0, ground_id)
|
| 77 |
-
print("β
Created ground plane")
|
| 78 |
-
return True
|
| 79 |
-
except Exception as e:
|
| 80 |
-
print(f"β Failed to create ground: {str(e)}")
|
| 81 |
-
return False
|
| 82 |
-
|
| 83 |
-
def create_robot(self, robot_specs: Dict) -> bool:
|
| 84 |
-
"""Create a robot from specifications"""
|
| 85 |
-
try:
|
| 86 |
-
# Create body
|
| 87 |
-
body_specs = robot_specs["body"]
|
| 88 |
-
body_collision = p.createCollisionShape(
|
| 89 |
-
p.GEOM_BOX,
|
| 90 |
-
halfExtents=[d/2 for d in body_specs["dimensions"]]
|
| 91 |
-
)
|
| 92 |
-
body_visual = p.createVisualShape(
|
| 93 |
-
p.GEOM_BOX,
|
| 94 |
-
halfExtents=[d/2 for d in body_specs["dimensions"]],
|
| 95 |
-
rgbaColor=body_specs.get("color", [0.8, 0.2, 0.2, 1])
|
| 96 |
-
)
|
| 97 |
-
self.robot_id = p.createMultiBody(
|
| 98 |
-
baseMass=body_specs["mass"],
|
| 99 |
-
baseCollisionShapeIndex=body_collision,
|
| 100 |
-
baseVisualShapeIndex=body_visual,
|
| 101 |
-
basePosition=[0, 0, body_specs["dimensions"][2]/2]
|
| 102 |
-
)
|
| 103 |
-
|
| 104 |
-
# Create wheels
|
| 105 |
-
self.wheel_ids = []
|
| 106 |
-
self.joint_ids = []
|
| 107 |
-
for i, wheel_spec in enumerate(robot_specs["wheels"]):
|
| 108 |
-
pos_x, pos_y, pos_z = wheel_spec["position"]
|
| 109 |
-
|
| 110 |
-
# Create wheel
|
| 111 |
-
wheel_collision = p.createCollisionShape(
|
| 112 |
-
p.GEOM_CYLINDER,
|
| 113 |
-
radius=float(wheel_spec["radius"]),
|
| 114 |
-
height=float(wheel_spec["width"])
|
| 115 |
-
)
|
| 116 |
-
wheel_visual = p.createVisualShape(
|
| 117 |
-
p.GEOM_CYLINDER,
|
| 118 |
-
radius=float(wheel_spec["radius"]),
|
| 119 |
-
length=float(wheel_spec["width"]),
|
| 120 |
-
rgbaColor=wheel_spec.get("color", [0.2, 0.2, 0.2, 1])
|
| 121 |
-
)
|
| 122 |
-
wheel_id = p.createMultiBody(
|
| 123 |
-
baseMass=wheel_spec.get("mass", 1.0),
|
| 124 |
-
baseCollisionShapeIndex=wheel_collision,
|
| 125 |
-
baseVisualShapeIndex=wheel_visual,
|
| 126 |
-
basePosition=[float(pos_x), float(pos_y), float(pos_z)]
|
| 127 |
-
)
|
| 128 |
-
self.wheel_ids.append(wheel_id)
|
| 129 |
-
|
| 130 |
-
# Create joint
|
| 131 |
-
joint_id = p.createConstraint(
|
| 132 |
-
self.robot_id,
|
| 133 |
-
wheel_id,
|
| 134 |
-
p.JOINT_POINT2POINT,
|
| 135 |
-
[0, 0, 0],
|
| 136 |
-
[float(pos_x), float(pos_y), float(pos_z)],
|
| 137 |
-
[0, 0, 0]
|
| 138 |
-
)
|
| 139 |
-
self.joint_ids.append(joint_id)
|
| 140 |
-
|
| 141 |
-
print(f"β
Created robot with {len(self.wheel_ids)} wheels")
|
| 142 |
-
return True
|
| 143 |
-
except Exception as e:
|
| 144 |
-
print(f"β Failed to create robot: {str(e)}")
|
| 145 |
-
return False
|
| 146 |
-
|
| 147 |
-
def setup_camera(self) -> bool:
|
| 148 |
-
"""Setup camera for visualization"""
|
| 149 |
-
try:
|
| 150 |
-
camera_config = self.config["camera"]
|
| 151 |
-
self.view_matrix = p.computeViewMatrix(
|
| 152 |
-
cameraEyePosition=camera_config["position"],
|
| 153 |
-
cameraTargetPosition=camera_config["target"],
|
| 154 |
-
cameraUpVector=camera_config["up"]
|
| 155 |
-
)
|
| 156 |
-
self.proj_matrix = p.computeProjectionMatrixFOV(
|
| 157 |
-
fov=camera_config["fov"],
|
| 158 |
-
aspect=float(camera_config["width"]) / camera_config["height"],
|
| 159 |
-
nearVal=camera_config["near"],
|
| 160 |
-
farVal=camera_config["far"]
|
| 161 |
-
)
|
| 162 |
-
print("β
Camera setup completed")
|
| 163 |
-
return True
|
| 164 |
-
except Exception as e:
|
| 165 |
-
print(f"β Failed to setup camera: {str(e)}")
|
| 166 |
-
return False
|
| 167 |
-
|
| 168 |
-
def capture_frame(self) -> Optional[str]:
|
| 169 |
-
"""Capture a frame from the simulation"""
|
| 170 |
-
try:
|
| 171 |
-
camera_config = self.config["camera"]
|
| 172 |
-
_, _, rgb, _, _ = p.getCameraImage(
|
| 173 |
-
width=camera_config["width"],
|
| 174 |
-
height=camera_config["height"],
|
| 175 |
-
viewMatrix=self.view_matrix,
|
| 176 |
-
projectionMatrix=self.proj_matrix,
|
| 177 |
-
renderer=p.ER_BULLET_HARDWARE_OPENGL
|
| 178 |
-
)
|
| 179 |
-
|
| 180 |
-
# Convert to base64
|
| 181 |
-
img = Image.fromarray(rgb)
|
| 182 |
-
buffered = io.BytesIO()
|
| 183 |
-
img.save(buffered, format="PNG")
|
| 184 |
-
return base64.b64encode(buffered.getvalue()).decode()
|
| 185 |
-
except Exception as e:
|
| 186 |
-
print(f"β Failed to capture frame: {str(e)}")
|
| 187 |
-
return None
|
| 188 |
-
|
| 189 |
-
def run_simulation(self, duration_sec: float = 7, capture_interval: int = 100) -> List[str]:
|
| 190 |
-
"""Run the simulation and capture frames"""
|
| 191 |
-
try:
|
| 192 |
-
frames = []
|
| 193 |
-
num_steps = int(duration_sec * 240) # 240 Hz simulation
|
| 194 |
-
|
| 195 |
-
for step in range(num_steps):
|
| 196 |
-
p.stepSimulation()
|
| 197 |
-
|
| 198 |
-
if step % capture_interval == 0:
|
| 199 |
-
frame = self.capture_frame()
|
| 200 |
-
if frame:
|
| 201 |
-
frames.append(frame)
|
| 202 |
-
print(f"Captured frame at step {step}/{num_steps}")
|
| 203 |
-
|
| 204 |
-
if step % 100 == 0:
|
| 205 |
-
print(f"Simulation step {step}/{num_steps}")
|
| 206 |
-
|
| 207 |
-
return frames
|
| 208 |
-
except Exception as e:
|
| 209 |
-
print(f"β Simulation failed: {str(e)}")
|
| 210 |
-
return []
|
| 211 |
-
|
| 212 |
-
def get_robot_state(self) -> Optional[Dict[str, Any]]:
|
| 213 |
-
"""Get current state of the robot"""
|
| 214 |
-
try:
|
| 215 |
-
if self.robot_id is None:
|
| 216 |
-
return None
|
| 217 |
-
|
| 218 |
-
pos, quat = p.getBasePositionAndOrientation(self.robot_id)
|
| 219 |
-
vel, ang_vel = p.getBaseVelocity(self.robot_id)
|
| 220 |
-
|
| 221 |
-
return {
|
| 222 |
-
"position": pos,
|
| 223 |
-
"orientation": quat,
|
| 224 |
-
"linear_velocity": vel,
|
| 225 |
-
"angular_velocity": ang_vel
|
| 226 |
-
}
|
| 227 |
-
except Exception as e:
|
| 228 |
-
print(f"β Failed to get robot state: {str(e)}")
|
| 229 |
-
return None
|
| 230 |
-
|
| 231 |
-
def apply_control(self, wheel_velocities: List[float]) -> bool:
|
| 232 |
-
"""Apply control signals to the robot"""
|
| 233 |
-
try:
|
| 234 |
-
if len(wheel_velocities) != len(self.wheel_ids):
|
| 235 |
-
raise ValueError("Number of wheel velocities must match number of wheels")
|
| 236 |
-
|
| 237 |
-
for wheel_id, velocity in zip(self.wheel_ids, wheel_velocities):
|
| 238 |
-
p.setJointMotorControl2(
|
| 239 |
-
wheel_id,
|
| 240 |
-
0, # joint index
|
| 241 |
-
p.VELOCITY_CONTROL,
|
| 242 |
-
targetVelocity=velocity,
|
| 243 |
-
force=100
|
| 244 |
-
)
|
| 245 |
-
return True
|
| 246 |
-
except Exception as e:
|
| 247 |
-
print(f"β Failed to apply control: {str(e)}")
|
| 248 |
-
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/cursor_pybullet/record_simulation.py
DELETED
|
@@ -1,105 +0,0 @@
|
|
| 1 |
-
import cv2
|
| 2 |
-
import numpy as np
|
| 3 |
-
import base64
|
| 4 |
-
from io import BytesIO
|
| 5 |
-
from PIL import Image
|
| 6 |
-
import time
|
| 7 |
-
from mcp import CursorPyBulletMCP
|
| 8 |
-
|
| 9 |
-
def base64_to_frame(base64_string):
|
| 10 |
-
"""Convert base64 string to numpy array for OpenCV"""
|
| 11 |
-
img_data = base64.b64decode(base64_string)
|
| 12 |
-
img = Image.open(BytesIO(img_data))
|
| 13 |
-
return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
|
| 14 |
-
|
| 15 |
-
def main():
|
| 16 |
-
# Create MCP instance
|
| 17 |
-
mcp = CursorPyBulletMCP()
|
| 18 |
-
|
| 19 |
-
# Connect to PyBullet
|
| 20 |
-
if not mcp.connect():
|
| 21 |
-
print("Failed to connect to PyBullet")
|
| 22 |
-
return
|
| 23 |
-
|
| 24 |
-
try:
|
| 25 |
-
# Create ground
|
| 26 |
-
if not mcp.create_ground():
|
| 27 |
-
print("Failed to create ground")
|
| 28 |
-
return
|
| 29 |
-
|
| 30 |
-
# Create robot
|
| 31 |
-
robot_specs = {
|
| 32 |
-
"body": {
|
| 33 |
-
"dimensions": [1.0, 0.5, 0.3],
|
| 34 |
-
"mass": 10.0,
|
| 35 |
-
"color": [0.8, 0.2, 0.2, 1.0] # Red color
|
| 36 |
-
},
|
| 37 |
-
"wheels": [
|
| 38 |
-
{
|
| 39 |
-
"position": [0.5, 0.3, 0.15],
|
| 40 |
-
"radius": 0.1,
|
| 41 |
-
"width": 0.05,
|
| 42 |
-
"color": [0.2, 0.2, 0.2, 1.0], # Dark gray
|
| 43 |
-
"mass": 1.0
|
| 44 |
-
},
|
| 45 |
-
{
|
| 46 |
-
"position": [0.5, -0.3, 0.15],
|
| 47 |
-
"radius": 0.1,
|
| 48 |
-
"width": 0.05,
|
| 49 |
-
"color": [0.2, 0.2, 0.2, 1.0],
|
| 50 |
-
"mass": 1.0
|
| 51 |
-
},
|
| 52 |
-
{
|
| 53 |
-
"position": [-0.5, 0.3, 0.15],
|
| 54 |
-
"radius": 0.1,
|
| 55 |
-
"width": 0.05,
|
| 56 |
-
"color": [0.2, 0.2, 0.2, 1.0],
|
| 57 |
-
"mass": 1.0
|
| 58 |
-
},
|
| 59 |
-
{
|
| 60 |
-
"position": [-0.5, -0.3, 0.15],
|
| 61 |
-
"radius": 0.1,
|
| 62 |
-
"width": 0.05,
|
| 63 |
-
"color": [0.2, 0.2, 0.2, 1.0],
|
| 64 |
-
"mass": 1.0
|
| 65 |
-
}
|
| 66 |
-
]
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
-
if not mcp.create_robot(robot_specs):
|
| 70 |
-
print("Failed to create robot")
|
| 71 |
-
return
|
| 72 |
-
|
| 73 |
-
# Setup camera
|
| 74 |
-
if not mcp.setup_camera():
|
| 75 |
-
print("Failed to setup camera")
|
| 76 |
-
return
|
| 77 |
-
|
| 78 |
-
# Get camera settings
|
| 79 |
-
width = mcp.config["camera"]["width"]
|
| 80 |
-
height = mcp.config["camera"]["height"]
|
| 81 |
-
|
| 82 |
-
# Setup video writer
|
| 83 |
-
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
| 84 |
-
out = cv2.VideoWriter('simulation.mp4', fourcc, 30.0, (width, height))
|
| 85 |
-
|
| 86 |
-
# Run simulation with more frequent frame capture
|
| 87 |
-
print("Starting simulation recording...")
|
| 88 |
-
frames = mcp.run_simulation(duration_sec=10, capture_interval=10) # Capture every 10 steps
|
| 89 |
-
|
| 90 |
-
# Convert frames to video
|
| 91 |
-
for i, frame_base64 in enumerate(frames):
|
| 92 |
-
frame = base64_to_frame(frame_base64)
|
| 93 |
-
out.write(frame)
|
| 94 |
-
print(f"Processed frame {i+1}/{len(frames)}")
|
| 95 |
-
|
| 96 |
-
# Release video writer
|
| 97 |
-
out.release()
|
| 98 |
-
print("β
Video saved as 'simulation.mp4'")
|
| 99 |
-
|
| 100 |
-
finally:
|
| 101 |
-
# Always disconnect
|
| 102 |
-
mcp.disconnect()
|
| 103 |
-
|
| 104 |
-
if __name__ == "__main__":
|
| 105 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mcp/cursor_pybullet/setup.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
from setuptools import setup, find_packages
|
| 2 |
-
|
| 3 |
-
setup(
|
| 4 |
-
name="cursor_pybullet_mcp",
|
| 5 |
-
version="1.0.0",
|
| 6 |
-
packages=find_packages(),
|
| 7 |
-
install_requires=[
|
| 8 |
-
"pybullet>=3.2.5",
|
| 9 |
-
"numpy>=1.24.3",
|
| 10 |
-
"Pillow>=10.0.0",
|
| 11 |
-
"opencv-python>=4.8.0"
|
| 12 |
-
],
|
| 13 |
-
author="Your Name",
|
| 14 |
-
author_email="your.email@example.com",
|
| 15 |
-
description="Cursor-PyBullet MCP for robot simulation",
|
| 16 |
-
long_description=open("README.md").read(),
|
| 17 |
-
long_description_content_type="text/markdown",
|
| 18 |
-
url="https://github.com/yourusername/cursor_pybullet_mcp",
|
| 19 |
-
classifiers=[
|
| 20 |
-
"Programming Language :: Python :: 3",
|
| 21 |
-
"License :: OSI Approved :: MIT License",
|
| 22 |
-
"Operating System :: OS Independent",
|
| 23 |
-
],
|
| 24 |
-
python_requires=">=3.7",
|
| 25 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|