11import os
2+ import time
3+ import webbrowser
24import rtde_control
35import rtde_receive
46import numpy as np
2729LEFT_ROBOT_IP = "192.168.50.55"
2830RIGHT_ROBOT_IP = "192.168.50.195"
2931
30- SERVO_TIME = 0.017 # 17ms (60Hz)
31- LOOKAHEAD_TIME = 0.1 # 100ms look ahead
32- SERVO_GAIN = 300.0 # Servo gain
33- MAX_VELOCITY = 0.5 # 0.5 m/s
34- MAX_ACCELERATION = 1.0 # 1.0 m/s^2
32+ SERVO_TIME = 0.017
33+ LOOKAHEAD_TIME = 0.1
34+ SERVO_GAIN = 300.0
35+ MAX_VELOCITY = 0.5
36+ MAX_ACCELERATION = 1.0
3537
3638GRIPPER_FORCE = 128
3739GRIPPER_SPEED = 255
38- CONTROLLER_DEADZONE = 0.1 # Deadzone for controller input
40+ CONTROLLER_DEADZONE = 0.1
3941
4042LEFT_INITIAL_JOINT_DEG = np .array (
4143 [165.26 , - 47.50 , 118.93 , - 38.96 , 87.51 , 149.56 ]
@@ -83,15 +85,16 @@ def __init__(
8385
8486 self .gripper = RobotiqGripper ()
8587 self .gripper .connect (robot_ip , 63352 )
86- self .gripper .activate ()
8788 print ("Gripper connected." )
8889
89- def move_to_initial_position (self ):
90+ def reset (self ):
9091 print (
9192 f"Moving to initial joint positions: { self .initial_joint_positions } "
9293 )
9394 self .rtde_c .moveJ (self .initial_joint_positions )
9495 print ("Reached initial position." )
96+ self .gripper .activate ()
97+ print ("Gripper activated." )
9598
9699 def servo_joints (self , joint_positions : np .ndarray ):
97100 t_start = self .rtde_c .initPeriod ()
@@ -125,11 +128,11 @@ def get_current_joint_positions(self) -> np.ndarray:
125128 def get_current_tcp_pose (self ) -> np .ndarray :
126129 return np .array (self .rtde_r .getActualTCPPose ())
127130
128- def disconnect (self ):
131+ def close (self ):
129132 self .rtde_c .servoStop ()
130133 self .rtde_c .stopScript ()
131134 self .gripper .disconnect ()
132- print ("UR controller stopped and gripper disconnected." )
135+ print ("UR controller closed and gripper disconnected." )
133136
134137
135138class DualArmURController :
@@ -184,16 +187,16 @@ def __init__(
184187 # Placo Setup
185188 self .placo_robot = placo .RobotWrapper (self .robot_urdf_path )
186189 self .solver = placo .KinematicsSolver (self .placo_robot )
187- self .solver .dt = servo_time # Or use servo_time
188- self .solver .mask_fbase (True ) # UR arms are fixed base
190+ self .solver .dt = servo_time
191+ self .solver .mask_fbase (True )
189192 self .solver .add_kinetic_energy_regularization_task (1e-6 )
190193
191194 # Define end-effector configuration (adjust link names and pico sources as needed)
192195 self .end_effector_config = {
193196 "left_arm" : {
194- "link_name" : "left_tool0" , # Link name in your URDF for left EE
195- "pose_source" : "left_controller" , # Key in pico_client for left controller pose
196- "control_trigger" : "left_grip" , # Key for left grip
197+ "link_name" : "left_tool0" ,
198+ "pose_source" : "left_controller" ,
199+ "control_trigger" : "left_grip" ,
197200 "gripper_trigger" : "left_trigger" ,
198201 "vis_target" : "left_target" ,
199202 },
@@ -212,8 +215,6 @@ def __init__(
212215 self .init_controller_xyz = {}
213216 self .init_controller_quat = {}
214217 for name , config in self .end_effector_config .items ():
215- # Initialize tasks with current robot pose (or a default if robot not moved yet)
216- # For simplicity, initializing with identity, will be updated on first activation
217218 initial_pose = np .eye (4 )
218219 self .effector_task [name ] = self .solver .add_frame_task (
219220 config ["link_name" ], initial_pose
@@ -246,6 +247,13 @@ def __init__(
246247 if self .visualize_placo :
247248 self .placo_robot .update_kinematics ()
248249 self .placo_vis = robot_viz (self .placo_robot )
250+
251+ # Automatically open browser window
252+ time .sleep (0.5 ) # Small delay to ensure server is ready
253+ meshcat_url = self .placo_vis .viewer .url ()
254+ print (f"Automatically opening meshcat at: { meshcat_url } " )
255+ webbrowser .open (meshcat_url )
256+
249257 self .placo_vis .display (self .placo_robot .state .q )
250258 for name , config in self .end_effector_config .items ():
251259 robot_frame_viz (self .placo_robot , config ["link_name" ])
@@ -387,6 +395,7 @@ def calc_target_joint_position(self):
387395 if self .visualize_placo and hasattr (self , "placo_vis" ):
388396 self .placo_vis .display (self .placo_robot .state .q )
389397 for name , config in self .end_effector_config .items ():
398+ robot_frame_viz (self .placo_robot , config ["link_name" ])
390399 frame_viz (
391400 f"target_{ name } " ,
392401 self .effector_task [name ].T_world_frame ,
@@ -401,13 +410,13 @@ def calc_target_joint_position(self):
401410 f"An unexpected error occurred in IK: { e } . Returning last known good joint positions."
402411 )
403412
404- def move_to_initial_positions (self ):
405- self .left_controller .move_to_initial_position ()
406- self .right_controller .move_to_initial_position ()
413+ def reset (self ):
414+ self .left_controller .reset ()
415+ self .right_controller .reset ()
407416
408- def disconnect (self ):
409- self .left_controller .disconnect ()
410- self .right_controller .disconnect ()
417+ def close (self ):
418+ self .left_controller .close ()
419+ self .right_controller .close ()
411420
412421 def run_left_controller_thread (self , stop_event ):
413422 print ("Starting left arm control thread..." )
@@ -418,7 +427,7 @@ def run_left_controller_thread(self, stop_event):
418427 self .left_controller .gripper_speed ,
419428 self .left_controller .gripper_force ,
420429 )
421- self .left_controller .disconnect ()
430+ self .left_controller .close ()
422431
423432 def run_right_controller_thread (self , stop_event ):
424433 print ("Starting right arm control thread..." )
@@ -429,40 +438,23 @@ def run_right_controller_thread(self, stop_event):
429438 self .right_controller .gripper_speed ,
430439 self .right_controller .gripper_force ,
431440 )
432- self .right_controller .disconnect ()
441+ self .right_controller .close ()
433442
434- def run (self ): # Modified to remove get_joint_positions_func
443+ def run (self , stop_event = threading . Event ()):
435444 try :
436- self .move_to_initial_positions ()
437- # Ensure Placo's initial q state matches the robot after moving to initial
438- left_q_init_actual = (
439- self .left_controller .get_current_joint_positions ()
440- )
441- print (f"Left initial joint positions: { left_q_init_actual } " )
442- right_q_init_actual = (
443- self .right_controller .get_current_joint_positions ()
444- )
445- print (f"Right initial joint positions: { right_q_init_actual } " )
446-
447- self .placo_robot .state .q [7 :13 ] = left_q_init_actual
448- self .placo_robot .state .q [13 :19 ] = right_q_init_actual
449-
450- self .placo_robot .update_kinematics () # Update placo model with these initial positions
451-
452- self .target_left_q = left_q_init_actual .copy ()
453- self .target_right_q = right_q_init_actual .copy ()
445+ self .reset ()
446+ self .calc_target_joint_position ()
454447
455448 except Exception as e :
456449 print (
457450 f"Error moving to initial positions or setting up Placo initial state: { e } "
458451 )
459- self .disconnect ()
452+ self .close ()
460453 return
461454
462455 print ("Starting dual-arm control loop..." )
463456
464457 try :
465- stop_event = threading .Event ()
466458 left_thread = threading .Thread (
467459 target = self .run_left_controller_thread ,
468460 args = (stop_event ,),
@@ -480,11 +472,10 @@ def run(self): # Modified to remove get_joint_positions_func
480472 except KeyboardInterrupt :
481473 print ("Keyboard interrupt received. Stopping control loop." )
482474 stop_event .set ()
483- left_thread .join ()
484- right_thread .join ()
485- except KeyboardInterrupt :
486- print ("Keyboard interrupt received. Stopping control loop." )
475+
476+ left_thread .join ()
477+ right_thread .join ()
487478 except Exception as e :
488479 print (f"Exception during control loop: { e } " )
489480
490- self .disconnect ()
481+ self .close ()
0 commit comments