Skip to content

Commit 4272b98

Browse files
authored
Merge pull request #40 from PCrnjak/feat/mujoco-world-model
Carry the seventh shape wire element and refuse physics explicitly
2 parents b776ea3 + a3a2cd2 commit 4272b98

10 files changed

Lines changed: 251 additions & 221 deletions

File tree

‎.github/workflows/tests.yml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,12 @@ jobs:
144144
PYTHONUTF8: '1'
145145
run: |
146146
pytest
147+
# The examples are the scripts a user copies, and `--examples` is opt-in,
148+
# so nothing was running them: four had rotted into refusals on their
149+
# own happy path. They run each script as a subprocess against the
150+
# simulator, so they belong after the suite rather than inside it.
151+
- name: Run examples
152+
env:
153+
PYTHONUNBUFFERED: '1'
154+
PYTHONUTF8: '1'
155+
run: pytest tests/test_examples.py --examples

‎examples/demo_showcase.py‎

Lines changed: 141 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -2,157 +2,147 @@
22
33
Exercises move_j, move_l, move_c, move_p, move_s, blended zig-zag,
44
tool actions, TCP offset, and precision TRF rotations.
5-
6-
Run:
7-
python examples/demo_showcase.py
85
"""
96

107
import math
11-
12-
from parol6 import Robot
13-
14-
HOST = "127.0.0.1"
15-
PORT = 5001
16-
17-
with Robot(host=HOST, port=PORT, normalize_logs=True) as robot:
18-
rbt = robot.create_sync_client(timeout=2.0)
19-
rbt.wait_ready(timeout=5.0)
20-
rbt.simulator(True)
21-
22-
# Select tool and home
23-
rbt.select_tool("SSG-48")
24-
rbt.tool.calibrate()
25-
rbt.home(wait=True)
26-
27-
# move_j vs move_l (joint-space then linear-cartesian to nearby pose)
28-
rbt.move_j(pose=[100, 240, 334, 90, 0, 90], speed=0.5, wait=True)
29-
rbt.move_l([-50, 240, 334, 90, 0, 90], speed=0.5, wait=True)
30-
31-
# ── Curved motion: three vertical circles + sine-wave spline ──────────
32-
RADIUS = 30
33-
SPEED = 0.4
34-
CIRCLE_Y = 340
35-
ORIENTATION = [90, 0, 90]
36-
CENTERS = [(0, CIRCLE_Y, 280), (0, CIRCLE_Y, 210), (0, CIRCLE_Y, 140)]
37-
38-
def circle_pt(cx, cz, angle_deg):
39-
"""Circle in the XZ plane (vertical) at fixed Y."""
40-
a = math.radians(angle_deg)
41-
return [
42-
cx + RADIUS * math.cos(a),
43-
CIRCLE_Y,
44-
cz + RADIUS * math.sin(a),
45-
] + ORIENTATION
46-
47-
# Circle 1: full circle with a single move_c (start = end)
48-
cx, _, cz = CENTERS[0]
49-
rbt.move_j(pose=circle_pt(cx, cz, 0), speed=0.5, wait=True)
50-
rbt.move_c(
51-
via=circle_pt(cx, cz, 180), end=circle_pt(cx, cz, 0), speed=SPEED, wait=True
52-
)
53-
54-
# Circle 2: two half-circle move_c arcs
55-
cx, _, cz = CENTERS[1]
56-
rbt.move_l(circle_pt(cx, cz, 0), speed=SPEED, wait=True)
57-
rbt.move_c(
58-
via=circle_pt(cx, cz, 90), end=circle_pt(cx, cz, 180), speed=SPEED, wait=True
59-
)
60-
rbt.move_c(
61-
via=circle_pt(cx, cz, 270), end=circle_pt(cx, cz, 0), speed=SPEED, wait=True
62-
)
63-
64-
# Circle 3: computed waypoints with move_p
65-
cx, _, cz = CENTERS[2]
66-
waypoints = [circle_pt(cx, cz, i * 30) for i in range(12)]
67-
waypoints.append(waypoints[0])
68-
rbt.move_l(waypoints[0], speed=SPEED, wait=True)
69-
rbt.move_p(waypoints, speed=SPEED, wait=True)
70-
71-
# Sine wave through all three circle centers (bottom to top) using move_s
72-
SINE_POINTS = 36
73-
z_min, z_max = CENTERS[2][2], CENTERS[0][2]
74-
spline = []
75-
for i in range(SINE_POINTS + 1):
76-
t = i / SINE_POINTS
77-
z = z_min + t * (z_max - z_min)
78-
x = RADIUS * math.cos(t * 3 * 2 * math.pi)
79-
spline.append([x, CIRCLE_Y, z] + ORIENTATION)
80-
rbt.move_s(spline, speed=SPEED, wait=True)
81-
82-
# ── Zig-zag scan ─────────────────────────────────────────────────────
83-
ZZ_ORI = [-180, -90, -180]
84-
ROWS = 6
85-
Y_MIN, Y_MAX = 0, 160
86-
Z_MIN, Z_MAX = 200, 300
87-
X = 280
88-
BLEND = 15
89-
90-
rbt.move_j(pose=[X, 0, 334] + ZZ_ORI, speed=0.5, wait=True)
91-
rbt.move_l([X, Y_MIN, Z_MAX + 30] + ZZ_ORI, speed=0.5, wait=True)
92-
z_step = (Z_MAX - Z_MIN) / (ROWS - 1)
93-
for row in range(ROWS):
94-
z = Z_MAX - row * z_step
95-
is_last = row == ROWS - 1
96-
y_start, y_end = (Y_MIN, Y_MAX) if row % 2 == 0 else (Y_MAX, Y_MIN)
97-
rbt.move_l([X, y_start, z] + ZZ_ORI, speed=0.5, r=BLEND, wait=False)
98-
rbt.move_l(
99-
[X, y_end, z] + ZZ_ORI, speed=0.5, r=0 if is_last else BLEND, wait=False
100-
)
101-
rbt.wait_motion()
102-
103-
# ── Precision demo: pencil pick-up and TCP-offset rotations ──────────
104-
# Home first — orientation flip from zigzag end requires fresh joint config.
105-
rbt.home(wait=True)
106-
PRECISION_POSE = [0, -250, 350, -90, 0, -90]
107-
rbt.move_j(pose=PRECISION_POSE, speed=0.5, wait=True)
108-
109-
# Test gripper: two quick close/open cycles
110-
rbt.tool.close(speed=1.0)
111-
rbt.tool.open(speed=1.0)
112-
rbt.tool.close(speed=1.0)
113-
rbt.tool.open(speed=1.0)
114-
115-
# Approach pencil: move_j to 100mm above, descend linearly, grab, retract
116-
PENCIL_ABOVE = [-90, -81.6, 161.8, 0, -69.4, 180]
117-
rbt.move_j(angles=PENCIL_ABOVE, speed=0.3, wait=True)
118-
rbt.move_l([0, 0, -100, 0, 0, 0], rel=True, speed=0.2, wait=True)
119-
rbt.tool.close(wait=True)
120-
rbt.move_l([0, 0, 100, 0, 0, 0], rel=True, speed=0.2, wait=True)
121-
rbt.move_j(pose=PRECISION_POSE, speed=0.3, wait=True)
122-
123-
# Offset TCP to pencil tip (~100mm exposed below gripper)
124-
rbt.set_tcp_offset(0, 0, -100)
125-
126-
# Pencil tip traces straight lines (linear precision demo)
127-
# Forward/back (tool Z = world -Y at this pose)
128-
rbt.move_l([0, 0, 100, 0, 0, 0], speed=0.3, frame="TRF", rel=True, wait=True)
129-
rbt.move_l([0, 0, -200, 0, 0, 0], speed=0.3, frame="TRF", rel=True, wait=True)
130-
rbt.move_l([0, 0, 100, 0, 0, 0], speed=0.3, frame="TRF", rel=True, wait=True)
131-
# Side to side (tool Y = world -X at this pose)
132-
rbt.move_l([0, 60, 0, 0, 0, 0], speed=0.3, frame="TRF", rel=True, wait=True)
133-
rbt.move_l([0, -120, 0, 0, 0, 0], speed=0.3, frame="TRF", rel=True, wait=True)
134-
rbt.move_l([0, 60, 0, 0, 0, 0], speed=0.3, frame="TRF", rel=True, wait=True)
135-
136-
# Precision TRF rotations — pencil tip stays stationary while wrist rotates
137-
SWEEP = 20
138-
for axis in range(3):
139-
delta = [0, 0, 0, 0, 0, 0]
140-
delta[3 + axis] = -SWEEP
141-
rbt.move_l(delta, speed=0.5, frame="TRF", rel=True, wait=True)
142-
delta[3 + axis] = SWEEP
143-
rbt.move_l(delta, speed=0.5, frame="TRF", rel=True, wait=True)
144-
rbt.move_l(delta, speed=0.5, frame="TRF", rel=True, wait=True)
145-
delta[3 + axis] = -SWEEP
146-
rbt.move_l(delta, speed=0.5, frame="TRF", rel=True, wait=True)
147-
148-
# Place pencil back: descend linearly, release, retract
149-
rbt.set_tcp_offset(0, 0, 0)
150-
rbt.move_j(angles=PENCIL_ABOVE, speed=0.3, wait=True)
151-
rbt.move_l([0, 0, -100, 0, 0, 0], rel=True, speed=0.2, wait=True)
152-
rbt.tool.open(wait=True)
153-
rbt.move_l([0, 0, 100, 0, 0, 0], rel=True, speed=0.2, wait=True)
154-
155-
# Return and finish
156-
rbt.move_j(pose=PRECISION_POSE, speed=0.3, wait=True)
157-
rbt.home(wait=True)
158-
print("Done!")
8+
from parol6 import RobotClient
9+
10+
rbt = RobotClient()
11+
12+
HOME_ANGLES = [90.0, -90.0, 180.0, 0.0, 0.0, 180.0]
13+
HOME_TOLERANCE_DEG = 2.0
14+
15+
# Select tool, and home only if not already near the home pose
16+
rbt.select_tool("SSG-48")
17+
rbt.tool.calibrate()
18+
current = rbt.angles()
19+
if (
20+
current is None
21+
or max(abs(a - h) for a, h in zip(current, HOME_ANGLES)) > HOME_TOLERANCE_DEG
22+
):
23+
rbt.home()
24+
25+
# move_j vs move_l (joint-space then linear-cartesian to nearby pose)
26+
rbt.move_j(pose=[100, 340, 334, 90, 0, 90], speed=0.5)
27+
rbt.move_l([-50, 340, 334, 90, 0, 90], speed=0.5)
28+
29+
30+
# ── Curved motion: three vertical circles + sine-wave spline ──────────
31+
RADIUS = 30
32+
SPEED = 0.8
33+
CIRCLE_Y = 340
34+
ORIENTATION = [90, 0, 90]
35+
CENTERS = [(0, CIRCLE_Y, 280), (0, CIRCLE_Y, 210), (0, CIRCLE_Y, 140)]
36+
37+
38+
def circle_pt(cx, cz, angle_deg):
39+
"""Circle in the XZ plane (vertical) at fixed Y."""
40+
a = math.radians(angle_deg)
41+
return [
42+
cx + RADIUS * math.cos(a),
43+
CIRCLE_Y,
44+
cz + RADIUS * math.sin(a),
45+
] + ORIENTATION
46+
47+
48+
# Circle 1: full circle with a single move_c (start = end)
49+
cx, _, cz = CENTERS[0]
50+
rbt.move_j(pose=circle_pt(cx, cz, 0), speed=0.5)
51+
rbt.move_c(via=circle_pt(cx, cz, 180), end=circle_pt(cx, cz, 0), speed=SPEED)
52+
53+
# Circle 2: two half-circle move_c arcs
54+
cx, _, cz = CENTERS[1]
55+
rbt.move_l(circle_pt(cx, cz, 0), speed=SPEED)
56+
rbt.move_c(via=circle_pt(cx, cz, 90), end=circle_pt(cx, cz, 180), speed=SPEED)
57+
rbt.move_c(via=circle_pt(cx, cz, 270), end=circle_pt(cx, cz, 0), speed=SPEED)
58+
59+
# Circle 3: computed waypoints with move_p
60+
cx, _, cz = CENTERS[2]
61+
waypoints = [circle_pt(cx, cz, i * 30) for i in range(12)]
62+
waypoints.append(waypoints[0])
63+
rbt.move_l(waypoints[0], speed=SPEED)
64+
rbt.move_p(waypoints, speed=SPEED)
65+
66+
# Sine wave through all three circle centers (bottom to top) using move_s
67+
SINE_POINTS = 36
68+
z_min, z_max = CENTERS[2][2], CENTERS[0][2]
69+
spline = []
70+
for i in range(SINE_POINTS + 1):
71+
t = i / SINE_POINTS
72+
z = z_min + t * (z_max - z_min)
73+
x = RADIUS * math.cos(t * 3 * 2 * math.pi)
74+
spline.append([x, CIRCLE_Y, z] + ORIENTATION)
75+
rbt.move_s(spline, speed=SPEED)
76+
77+
# ── Zig-zag scan ─────────────────────────────────────────────────────
78+
ZZ_ORI = [-180, -90, -180]
79+
ROWS = 6
80+
Y_MIN, Y_MAX = 0, 160
81+
Z_MIN, Z_MAX = 200, 300
82+
X = 280
83+
BLEND = 15
84+
85+
rbt.move_j(pose=[X, 0, 334] + ZZ_ORI, speed=0.5)
86+
rbt.move_l([X, Y_MIN, Z_MAX + 30] + ZZ_ORI, speed=1.0)
87+
z_step = (Z_MAX - Z_MIN) / (ROWS - 1)
88+
for row in range(ROWS):
89+
z = Z_MAX - row * z_step
90+
is_last = row == ROWS - 1
91+
y_start, y_end = (Y_MIN, Y_MAX) if row % 2 == 0 else (Y_MAX, Y_MIN)
92+
rbt.move_l([X, y_start, z] + ZZ_ORI, speed=1.0, r=BLEND, wait=False)
93+
rbt.move_l([X, y_end, z] + ZZ_ORI, speed=1.0, r=0 if is_last else BLEND, wait=False)
94+
rbt.wait_motion()
95+
96+
# ── Precision demo: pencil pick-up and TCP-offset rotations ──────────
97+
PRECISION_POSE = [0, -250, 350, -90, 0, -90]
98+
rbt.move_j(pose=PRECISION_POSE, speed=0.5)
99+
100+
# Test gripper: two quick close/open cycles
101+
rbt.tool.close(speed=1.0)
102+
rbt.tool.open(speed=1.0)
103+
rbt.tool.close(speed=1.0)
104+
rbt.tool.open(speed=1.0)
105+
106+
# Approach pencil: move_j to 100mm above, descend linearly, grab, retract
107+
PENCIL_ABOVE = [-90, -81.6, 161.8, 0, -69.4, 180]
108+
rbt.move_j(angles=PENCIL_ABOVE, speed=0.8)
109+
rbt.move_l([0, 0, -93, 0, 0, 0], rel=True, speed=0.4)
110+
rbt.tool.close(wait=True)
111+
rbt.move_l([0, 0, 93, 0, 0, 0], rel=True, speed=0.4)
112+
rbt.move_j(pose=PRECISION_POSE, speed=0.8)
113+
114+
# Offset TCP to pencil tip (~100mm exposed below gripper). The pencil is
115+
# clamped perpendicular to the gripper's jaw-closing direction, hanging
116+
# along tool -X — that's the axis the offset goes on, not Z.
117+
rbt.set_tcp_offset(-100, 0, 0)
118+
119+
# Pencil tip traces straight lines (linear precision demo)
120+
rbt.move_l([0, 0, 100, 0, 0, 0], speed=0.8, frame="TRF", rel=True)
121+
rbt.move_l([0, 0, -200, 0, 0, 0], speed=0.8, frame="TRF", rel=True)
122+
rbt.move_l([0, 0, 100, 0, 0, 0], speed=0.8, frame="TRF", rel=True)
123+
124+
# Precision TRF rotations — pencil tip stays stationary while wrist rotates.
125+
# 40° is the largest sweep that keeps every axis IK-reachable from this pose
126+
# with the 100mm pencil offset.
127+
SWEEP = 40
128+
for axis in range(3):
129+
delta = [0, 0, 0, 0, 0, 0]
130+
delta[3 + axis] = -SWEEP
131+
rbt.move_l(delta, speed=0.8, frame="TRF", rel=True)
132+
delta[3 + axis] = SWEEP
133+
rbt.move_l(delta, speed=0.8, frame="TRF", rel=True)
134+
rbt.move_l(delta, speed=0.8, frame="TRF", rel=True)
135+
delta[3 + axis] = -SWEEP
136+
rbt.move_l(delta, speed=0.8, frame="TRF", rel=True)
137+
138+
# Place pencil back: descend linearly, release, retract
139+
rbt.set_tcp_offset(0, 0, 0)
140+
rbt.move_j(angles=PENCIL_ABOVE, speed=0.8)
141+
rbt.move_l([0, 0, -93, 0, 0, 0], rel=True, speed=0.4)
142+
rbt.tool.open(wait=True)
143+
rbt.move_l([0, 0, 93, 0, 0, 0], rel=True, speed=0.4)
144+
145+
# Return to home position (joint move, not the full homing sequence)
146+
rbt.move_j(pose=PRECISION_POSE, speed=0.8)
147+
rbt.move_j(angles=HOME_ANGLES, speed=0.8)
148+
print("Done!")

‎examples/manage_server_demo.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ def main() -> None:
3232
print("simulator(True):", sim_on)
3333

3434
if sim_on:
35+
# Reference the robot first: planned motion is refused until
36+
# it is homed, whatever its reported angles look like.
37+
client.home(wait=True)
3538
# Small relative move: +3mm in Z over 0.8s
3639
moved = client.move_l([0, 0, 3, 0, 0, 0], rel=True, duration=0.8)
3740
print("move_l ->", moved)

0 commit comments

Comments
 (0)