Update app.py
Browse files
app.py
CHANGED
|
@@ -5,7 +5,7 @@ import matplotlib.pyplot as plt
|
|
| 5 |
import tempfile
|
| 6 |
import gradio as gr
|
| 7 |
|
| 8 |
-
# Setup
|
| 9 |
physicsClient = p.connect(p.DIRECT)
|
| 10 |
p.setAdditionalSearchPath(pybullet_data.getDataPath())
|
| 11 |
p.setGravity(0, 0, -9.8)
|
|
@@ -13,54 +13,44 @@ p.loadURDF("plane.urdf")
|
|
| 13 |
robot = p.loadURDF("franka_panda/panda.urdf", basePosition=[0, 0, 0])
|
| 14 |
|
| 15 |
def get_panda_joints(robot):
|
| 16 |
-
revolute = []
|
| 17 |
-
finger = []
|
| 18 |
for i in range(p.getNumJoints(robot)):
|
| 19 |
name = p.getJointInfo(robot, i)[1].decode()
|
| 20 |
joint_type = p.getJointInfo(robot, i)[2]
|
| 21 |
if joint_type == p.JOINT_REVOLUTE:
|
| 22 |
-
if "finger" in name
|
| 23 |
-
finger.append(i)
|
| 24 |
-
else:
|
| 25 |
-
revolute.append(i)
|
| 26 |
return revolute, finger
|
| 27 |
|
| 28 |
arm_joints, finger_joints = get_panda_joints(robot)
|
| 29 |
|
| 30 |
-
# Common function to simulate and render
|
| 31 |
def render_sim(joint_values, gripper_val):
|
| 32 |
-
# Set joint
|
| 33 |
for idx, tgt in zip(arm_joints[:4], joint_values):
|
| 34 |
p.setJointMotorControl2(robot, idx, p.POSITION_CONTROL, targetPosition=tgt)
|
| 35 |
-
|
| 36 |
for fj in finger_joints:
|
| 37 |
p.setJointMotorControl2(robot, fj, p.POSITION_CONTROL, targetPosition=gripper_val)
|
|
|
|
| 38 |
|
| 39 |
-
|
| 40 |
-
p.stepSimulation()
|
| 41 |
-
|
| 42 |
-
# Camera
|
| 43 |
width, height = 512, 512
|
| 44 |
view_matrix = p.computeViewMatrix([1.5, 0, 1], [0, 0, 0.5], [0, 0, 1])
|
| 45 |
proj_matrix = p.computeProjectionMatrixFOV(60, width / height, 0.1, 3.1)
|
| 46 |
_, _, img, _, _ = p.getCameraImage(width, height, view_matrix, proj_matrix)
|
| 47 |
rgb = np.reshape(img, (height, width, 4))[:, :, :3]
|
| 48 |
|
| 49 |
-
#
|
| 50 |
fig, ax = plt.subplots(figsize=(5, 5))
|
| 51 |
ax.imshow(rgb.astype(np.uint8))
|
| 52 |
ax.axis("off")
|
| 53 |
-
# Approx joint label positions (you may tweak)
|
| 54 |
labels = ["J1", "J2", "J3", "J4"]
|
| 55 |
positions = [(180, 400), (220, 320), (260, 240), (300, 160)]
|
| 56 |
for label, pos in zip(labels, positions):
|
| 57 |
ax.text(*pos, label, color="red", fontsize=12, fontweight="bold")
|
| 58 |
-
|
| 59 |
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
|
| 60 |
plt.savefig(tmp.name, bbox_inches='tight')
|
| 61 |
plt.close()
|
| 62 |
|
| 63 |
-
# Joint
|
| 64 |
joint_text = (
|
| 65 |
f"Joint Angles:\n"
|
| 66 |
f"J1 = {joint_values[0]:.2f} rad\n"
|
|
@@ -71,36 +61,36 @@ def render_sim(joint_values, gripper_val):
|
|
| 71 |
)
|
| 72 |
return tmp.name, joint_text
|
| 73 |
|
| 74 |
-
#
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
#
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
inputs=[
|
| 86 |
-
gr.Slider(-3.14, 3.14, value=0, label="Joint 1"),
|
| 87 |
-
gr.Slider(-3.14, 3.14, value=0, label="Joint 2"),
|
| 88 |
-
gr.Slider(-3.14, 3.14, value=0, label="Joint 3"),
|
| 89 |
-
gr.Slider(-3.14, 3.14, value=0, label="Joint 4"),
|
| 90 |
-
gr.Slider(0.0, 0.04, value=0.02, step=0.001, label="Gripper Opening")
|
| 91 |
-
],
|
| 92 |
-
outputs=[
|
| 93 |
-
gr.Image(type="filepath", label="Live Simulation View (with Joint Labels)"),
|
| 94 |
-
gr.Textbox(label="Live Joint States")
|
| 95 |
-
],
|
| 96 |
-
live=True,
|
| 97 |
-
title="Live Robot Control with Joint Labeling",
|
| 98 |
-
description="Control 4 joints and gripper. Click Reset to restore default position.",
|
| 99 |
-
)
|
| 100 |
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
demo.load(reset_robot, None, [demo.outputs[0], demo.outputs[1]], trigger="reset_btn")
|
| 104 |
|
| 105 |
-
|
| 106 |
-
demo.launch()
|
|
|
|
| 5 |
import tempfile
|
| 6 |
import gradio as gr
|
| 7 |
|
| 8 |
+
# Setup PyBullet simulation
|
| 9 |
physicsClient = p.connect(p.DIRECT)
|
| 10 |
p.setAdditionalSearchPath(pybullet_data.getDataPath())
|
| 11 |
p.setGravity(0, 0, -9.8)
|
|
|
|
| 13 |
robot = p.loadURDF("franka_panda/panda.urdf", basePosition=[0, 0, 0])
|
| 14 |
|
| 15 |
def get_panda_joints(robot):
|
| 16 |
+
revolute, finger = [], []
|
|
|
|
| 17 |
for i in range(p.getNumJoints(robot)):
|
| 18 |
name = p.getJointInfo(robot, i)[1].decode()
|
| 19 |
joint_type = p.getJointInfo(robot, i)[2]
|
| 20 |
if joint_type == p.JOINT_REVOLUTE:
|
| 21 |
+
(finger if "finger" in name else revolute).append(i)
|
|
|
|
|
|
|
|
|
|
| 22 |
return revolute, finger
|
| 23 |
|
| 24 |
arm_joints, finger_joints = get_panda_joints(robot)
|
| 25 |
|
|
|
|
| 26 |
def render_sim(joint_values, gripper_val):
|
| 27 |
+
# Set joint angles
|
| 28 |
for idx, tgt in zip(arm_joints[:4], joint_values):
|
| 29 |
p.setJointMotorControl2(robot, idx, p.POSITION_CONTROL, targetPosition=tgt)
|
|
|
|
| 30 |
for fj in finger_joints:
|
| 31 |
p.setJointMotorControl2(robot, fj, p.POSITION_CONTROL, targetPosition=gripper_val)
|
| 32 |
+
for _ in range(10): p.stepSimulation()
|
| 33 |
|
| 34 |
+
# Render camera
|
|
|
|
|
|
|
|
|
|
| 35 |
width, height = 512, 512
|
| 36 |
view_matrix = p.computeViewMatrix([1.5, 0, 1], [0, 0, 0.5], [0, 0, 1])
|
| 37 |
proj_matrix = p.computeProjectionMatrixFOV(60, width / height, 0.1, 3.1)
|
| 38 |
_, _, img, _, _ = p.getCameraImage(width, height, view_matrix, proj_matrix)
|
| 39 |
rgb = np.reshape(img, (height, width, 4))[:, :, :3]
|
| 40 |
|
| 41 |
+
# Plot image with joint labels
|
| 42 |
fig, ax = plt.subplots(figsize=(5, 5))
|
| 43 |
ax.imshow(rgb.astype(np.uint8))
|
| 44 |
ax.axis("off")
|
|
|
|
| 45 |
labels = ["J1", "J2", "J3", "J4"]
|
| 46 |
positions = [(180, 400), (220, 320), (260, 240), (300, 160)]
|
| 47 |
for label, pos in zip(labels, positions):
|
| 48 |
ax.text(*pos, label, color="red", fontsize=12, fontweight="bold")
|
|
|
|
| 49 |
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
|
| 50 |
plt.savefig(tmp.name, bbox_inches='tight')
|
| 51 |
plt.close()
|
| 52 |
|
| 53 |
+
# Joint state text
|
| 54 |
joint_text = (
|
| 55 |
f"Joint Angles:\n"
|
| 56 |
f"J1 = {joint_values[0]:.2f} rad\n"
|
|
|
|
| 61 |
)
|
| 62 |
return tmp.name, joint_text
|
| 63 |
|
| 64 |
+
# Launch Gradio with Blocks
|
| 65 |
+
with gr.Blocks(title="Live Robot Control with Reset") as demo:
|
| 66 |
+
gr.Markdown("## 🤖 Live Robot Control\nUse sliders below or click Reset to restore pose.")
|
| 67 |
+
|
| 68 |
+
with gr.Row():
|
| 69 |
+
j1 = gr.Slider(-3.14, 3.14, value=0, label="Joint 1", interactive=True)
|
| 70 |
+
j2 = gr.Slider(-3.14, 3.14, value=0, label="Joint 2", interactive=True)
|
| 71 |
+
j3 = gr.Slider(-3.14, 3.14, value=0, label="Joint 3", interactive=True)
|
| 72 |
+
j4 = gr.Slider(-3.14, 3.14, value=0, label="Joint 4", interactive=True)
|
| 73 |
+
gripper = gr.Slider(0.0, 0.04, value=0.02, step=0.001, label="Gripper Opening", interactive=True)
|
| 74 |
+
|
| 75 |
+
with gr.Row():
|
| 76 |
+
img_output = gr.Image(type="filepath", label="Simulation View")
|
| 77 |
+
text_output = gr.Textbox(label="Joint States")
|
| 78 |
+
|
| 79 |
+
# Update live
|
| 80 |
+
def live_update(j1, j2, j3, j4, gripper):
|
| 81 |
+
return render_sim([j1, j2, j3, j4], gripper)
|
| 82 |
|
| 83 |
+
j1.change(live_update, inputs=[j1, j2, j3, j4, gripper], outputs=[img_output, text_output])
|
| 84 |
+
j2.change(live_update, inputs=[j1, j2, j3, j4, gripper], outputs=[img_output, text_output])
|
| 85 |
+
j3.change(live_update, inputs=[j1, j2, j3, j4, gripper], outputs=[img_output, text_output])
|
| 86 |
+
j4.change(live_update, inputs=[j1, j2, j3, j4, gripper], outputs=[img_output, text_output])
|
| 87 |
+
gripper.change(live_update, inputs=[j1, j2, j3, j4, gripper], outputs=[img_output, text_output])
|
| 88 |
|
| 89 |
+
# Reset button
|
| 90 |
+
def reset():
|
| 91 |
+
return render_sim([0, 0, 0, 0], 0.02)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
+
reset_btn = gr.Button("🔄 Reset Robot")
|
| 94 |
+
reset_btn.click(fn=reset, inputs=[], outputs=[img_output, text_output])
|
|
|
|
| 95 |
|
| 96 |
+
demo.launch()
|
|
|