Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #! /usr/bin/env python
- # 1) Broadcast the demanded BHand states as a topic,
- # 2) Broadcast the demanded BHand states as an action-server: should be able to tell it when to start and give it a user-provided trajectory to follow.
- # 3) Start the ros-node in a seperate-process to allow all events in that process access to ros-topics: allow the cart-MPC access to the current cart-states when predicting the intial-conditions of the next time-step (which will be used as initial-conditions for the MPC)
- # 4) Set-up the original-model (cart-mpc in a seperate-process, and the QP-solver in the main-process (may need to eventually move it into a seperate-process since the main-process may be used in many-places))
- # import rospy
- # For creating the action-server
- # import actionlib
- import numpy as np
- import queue # imported for using queue.Empty exception
- import time
- # For bradcasting the demanded bhand-state, and to get the planned cart-trajectory to follow, along with the start-time for broadcasting the demanded EE-poses
- # from cart_mpc.msg import bhand_demand, BHand_dem_poseFeedback, BHand_dem_poseResult, BHand_dem_poseAction
- # Get cart and BHand pose
- # from gazebo_msgs.msg import LinkStates
- # For reading the off-setted Force-Torque reading
- # from geometry_msgs.msg import PoseStamped, WrenchStamped
- # For running the ros-node on a seperate process, and for ensuring that the data currently-available in said-variable are not in the middle of being written to by the topic-subscriber callback-function
- from multiprocessing import Lock, Process, Queue
- # EE-pose trajectory follower
- # from QP_mover_V1 import Diff_IK
- # For sensding the demanded joint velocities to the robot.
- # from SummitClass_V10_mujoco import SummitMover
- class BHand_dem_pose(object):
- # create messages that are used to publish feedback/result
- # _feedback = BHand_dem_poseFeedback()
- # _result = BHand_dem_poseResult()
- # Constructor: All variables and functions with the self. keyword are initialised in the constructor
- def __init__(self):
- # Initialize the Cart-MPC module
- #cart_mpc = Cart_MPC()
- # Initialize variables
- # Initial process-time
- self.init_process_time = 0 # sec
- # Cart-MPC time-step
- self.delta_t_mpc = 0.5 # sec
- # QP-solver time-step
- self.delta_t_ee = 0.5 # sec
- # Number of rounds
- self.num_rounds = 1
- # Length
- self.radius = 0.3
- # Starting angle
- self.starting_angle = 0
- gains_EE = [7.0, 1, 0.1, 0.1] # PID
- # Subscribe to the topic that will send the demanded EE-state.
- # Subscribe to the topic that will give the current WAM joint-states.
- # Subscribe to the topic that will give the current off-setted FT-sensor data
- # Publish joint velocites to the MM joints
- # self.mm_mover = SummitMover()
- # # Build the next feedback msg to be sent
- # self._feedback.cart_pose_error = PoseStamped()
- # # Initialize QP Solver
- # with self.nostdout(): # To mute the output of the qpOASES solver when it is being initialized
- # self.mob_man = Diff_IK(k_p=gains_EE[0],k_d=gains_EE[1], k_i=gains_EE[2], k_w=gains_EE[3], delta_t=self.delta_t_ee)
- # # Creates the action server
- # self._as = actionlib.SimpleActionServer("cart_mpc_as", BHand_dem_poseAction, self.goal_callback, False)
- # self._as.start()
- # rospy.loginfo("Ready to plan cart-motion ...")
- def goal_callback(self, goal):
- # rospy.loginfo("Starting to broadcast bhand demanded state ...")
- # Start-time
- self.start_time = time.time()+goal#goal.start_time
- # Initialize some variables
- success = True
- # Create Lock object to make sure there is no memory conflict!
- lock_state = Lock()
- # Prepare four queues:
- # 1) For providing values to a queue dedicated for the cart-MPC (2 Hz),
- # 2) For providing values to a queue dedicated for the QP-solver (20 Hz),
- # 3) For providing the cart-mpc with the current-state (2 Hz),
- # 4) For providing the QP-solver with the current-state (20 Hz),
- # Initialize the queues
- # Queue of in-coming current system-state (for feedback | @ 2 Hz)
- queue_of_curr_state_cart_mpc = Queue()
- # Queue of in-coming current system-state (for feedback | @ 20 Hz)
- queue_of_curr_state_qp_sol = Queue()
- # Queue to be sent to the QP-solver (@ 20 Hz)
- queue_of_ref_EE_states = Queue()
- # Queue to be sent out as action-server feedback (@ 2 Hz)
- queue_of_cart_pose_error = Queue()
- # Queue to be sent out as joint-velocities ( @ 20 Hz)
- queue_of_joint_vel = Queue()
- # Initialize the Queue
- # Current system-state
- curr_sys_state = np.zeros((16,1))#self.mm_mover.get_current_mm_state()
- # For the Cart-MPC (2 Hz)
- queue_of_curr_state_cart_mpc.put(curr_sys_state)
- # Next instant
- curr_state_cart_mpc_nxt_ist = self.init_process_time + self.delta_t_mpc
- # For the QP-Solver (20 Hz)
- queue_of_curr_state_qp_sol.put(curr_sys_state)
- # Next instant
- curr_state_qp_sol_nxt_ist = self.init_process_time + self.delta_t_ee
- # Reference EE-state
- # Initial joint velocity
- init_joint_vel = np.array([[0],[0],[0], # MB velocity
- [0],[0],[0],[0],[0],[0],[0]]) # WAM velocity
- # Initial EE position
- mat_w_G_10_curr = np.eye(4) #self.mob_man.curr_EE_pose(curr_sys_state[0:10,0])
- # Initial EE position and velocity (as a reference)
- ref_EE_state = np.concatenate((mat_w_G_10_curr[0:3,3:4],np.zeros((3,1)),np.zeros((6,1))),axis=0)
- # Add initial EE-state to the Queue (20 Hz)
- queue_of_ref_EE_states.put(ref_EE_state)
- # Joint velocity
- queue_of_joint_vel.put(init_joint_vel)
- # Next instant
- cart_pose_error_nxt_ist = self.init_process_time + self.delta_t_mpc
- # Initialize the process
- # Cart-MPC
- p_cart_mpc = Process(target=self.add_new_EE_pose, args=(queue_of_curr_state_cart_mpc, # In (2 Hz)
- queue_of_ref_EE_states, # Out (20 Hz)
- queue_of_cart_pose_error, # Out (2 Hz)
- lock_state)) # Pass the lock-state that will control the sequence in which the locked-variables are accessed
- # QP-Solver
- p_qp_solver = Process(target=self.follow_new_EE_pose, args=(queue_of_curr_state_qp_sol, # In (20 Hz)
- queue_of_ref_EE_states, # In (20 Hz)
- queue_of_joint_vel, # Out (20 Hz)
- lock_state)) # Pass the lock-state that will control the sequence in which the locked-variables are accessed
- # Start the process:
- # To add jobs
- p_cart_mpc.start()
- print("Adding EE-poses to go to ...")
- # rospy.loginfo("Adding EE-poses to go to ...")
- # To do jobs
- p_qp_solver.start()
- print("Going to said EE-poses ...")
- # rospy.loginfo("Going to said EE-poses ...")
- # Wait till the off-setted time has elapsed
- while((time.time()-self.start_time)<self.init_process_time):
- continue
- # Now start keeping track of when to populate the relevant queues
- # print(str(time.time()-self.start_time)+" Start")
- while True:
- # Check if preempted/cancelled
- # if self._as.is_preempt_requested():
- # rospy.logwarn('The base goal has been cancelled/preempted!')
- # self._as.set_preempted()
- # success = False
- # break
- # Update system-state input to the cart-mpc (@ 2 Hz)
- if ((time.time()-self.start_time)>curr_state_cart_mpc_nxt_ist):
- # Update new time
- curr_state_cart_mpc_nxt_ist = curr_state_cart_mpc_nxt_ist + self.delta_t_mpc
- # Current system-state
- curr_mm_state = np.zeros((16,1))#self.mm_mover.get_current_mm_state()
- try:
- # print(str(time.time()-self.start_time)+" Outside A: Before")
- # Finish getting and putting data in queue
- # Acquire the lock
- # with lock_state:
- # lock_state.acquire()
- # For the Cart-MPC (2 Hz)
- # queue_of_curr_state_cart_mpc.put(curr_mm_state) # Un-comment it when you need it.
- # Extract the cart pose-error into a pose-object and send it out as feedback
- # print("Getting cart_pose_error")
- cart_pose_error = queue_of_cart_pose_error.get_nowait()
- # print("cart_pose_error")
- # Release the lock
- # lock_state.release()
- # print(str(time.time()-self.start_time)+" Outside A: After")
- except queue.Empty:
- print("Finished! A")
- break
- else:
- a=1
- # print("Got cart_pose_error")
- # # Set the header
- # self._feedback.cart_pose_error.header.seq = cart_pose_error[0,0]
- # self._feedback.cart_pose_error.header.stamp = rospy.Time.now()
- # self._feedback.cart_pose_error.header.frame_id = "global-frame"
- # # Set the pose error
- # self._feedback.cart_pose_error.pose.position.x = 0
- # self._feedback.cart_pose_error.pose.position.y = 0
- # self._feedback.cart_pose_error.pose.position.z = 0
- # self._feedback.cart_pose_error.pose.orientation.x = 0
- # self._feedback.cart_pose_error.pose.orientation.y = 0
- # self._feedback.cart_pose_error.pose.orientation.z = 0
- # self._feedback.cart_pose_error.pose.orientation.w = 0
- # # publish the feedback
- # self._as.publish_feedback(self._feedback)
- # Update system-state input to the QP-Solver
- # Send the computed joint-velocities to the ros-topic (@ 20 Hz)
- if ((time.time()-self.start_time)>curr_state_qp_sol_nxt_ist):
- # Update new time
- curr_state_qp_sol_nxt_ist = curr_state_qp_sol_nxt_ist + self.delta_t_ee
- # Update new system-states
- # Current system-state
- curr_mm_state = np.zeros((16,1))#self.mm_mover.get_current_mm_state()
- # print(str(time.time()-self.start_time)+" Outside B: Before")
- # Finish getting and putting data in queue
- try:
- # Get current commanded joint-velocities
- # print("Getting new_joint_vel")
- new_joint_vel = queue_of_joint_vel.get_nowait()
- # print("new_joint_vel")
- except queue.Empty:
- print("Finished! B")
- break
- else:
- a=1
- #print("Got new_joint_vel")
- # Acquire the lock
- # with lock_state:
- # lock_state.acquire()
- # For the QP-Solver (20 Hz)
- queue_of_curr_state_qp_sol.put(curr_mm_state)
- # Release the lock
- # lock_state.release()
- # print(str(time.time()-self.start_time)+" Outside B: After")
- # Send the commanded joint velocities
- # self.mm_mover.summit_move_velocity(new_joint_vel)
- print(new_joint_vel)
- print("Send terminal velocity, since the if-statement checking the joint-vel was not updated : "+str(new_joint_vel))
- # Completing a process
- p_cart_mpc.join()
- # rospy.loginfo("Finished adding EE-poses to go to.")
- print("Finished adding EE-poses to go to.")
- p_qp_solver.join()
- # rospy.loginfo("Finished going to said EE-poses.")
- print("Finished going to said EE-poses.")
- # At this point, either the goal has been achieved (success==true)
- # or the client preempted the goal (success==false).
- # If success, then we publish the final result.
- # If not success, we do not publish anything in the result
- # if success:
- # self._result.cart_pose_error_final = self._feedback.cart_pose_error
- # rospy.loginfo('Successfully covered the planned trajectory' )
- # self._as.set_succeeded(self._result)
- def add_new_EE_pose(self,
- queue_of_curr_state_cart_mpc, # Queue of in-coming current system-state (for feedback | @ 2 Hz)
- queue_of_ref_EE_states, # Queue to be sent to the QP-solver (@ 2 Hz)
- queue_of_cart_pose_error, # Queue to be sent out as action-server feedback (@ 2 Hz)
- lock_state): # Pass the lock-state that will control the sequence in which the locked-variables are accessed
- # Next time-instant for adding an EE-pose.
- next_time_inst = self.init_process_time + self.delta_t_mpc
- # Number of instances per revolution
- num_instances_rev = 500
- # Number of instances
- num_instances = 5#num_instances_rev*self.num_rounds
- # Wait till the off-setted time has elapsed
- while((time.time()-self.start_time)<self.init_process_time):
- continue
- # Keep adding velocities to the queue.
- for instant_ind in range(num_instances):
- # Finish getting data from queue
- # Acquire the lock
- # self.lock_state.acquire()
- # System joint position (from ROS-topics)
- # Wait for the data to be loaded
- # curr_joint_pos = queue_of_curr_state_cart_mpc.get()
- # Release the lock
- # self.lock_state.release()
- # New theta
- curr_theta = self.starting_angle + 2*np.pi*(instant_ind/num_instances_rev)
- # Reference position
- ref_EE_position = np.array([[0.2],
- [self.radius*np.sin(curr_theta)],
- [0.6]])
- # print(ref_EE_position)
- # Reference velocity
- ref_EE_velocity = np.array([[0],
- [self.radius*2*np.pi/num_instances_rev/self.delta_t_mpc*np.cos(curr_theta)],
- [0]])
- # Reference wrench (at the FT-sensor, the wrench experienced by it, NOT the wrench experienced by the BHand Free-body)
- ref_EE_wrench = np.array([[0], # This force-component must NOT be included in the cost function, as we will be imposing position-control to allow the BHand palm to follow a desired trajectory along that direction.
- [-1], # The BHand will be attempting to exert this magnitude of force on the horizontal handle.
- [0], # This force-component must NOT be included in the cost function, as we will be imposing position-control to make the BHand palm remain at a fixed height from the ground.
- [0], # This torque-component must NOT be included in the cost function, as we will be imposing pose-control to make the BHand palm remain vertical to the ground.
- [0], # This torque-component must NOT be included in the cost function, as we will be imposing pose-control to make the BHand palm remain vertical to the ground.
- [0]]) # The BHand must always be alighned with the horizontal-bar; not facing it would result in a torque to be experienced by the FT-sensor when the BHand comes in contact with it.
- # Reference state
- ref_EE_state = np.concatenate((ref_EE_position,ref_EE_velocity,ref_EE_wrench),axis=0)
- # Build the next feedback msg to be sent
- cart_pose_error = [instant_ind, # Set the header
- 0,0,0,0,0,0,0] # Set the pose error
- # print(str(time.time()-self.start_time)+" Inside A: Before")
- # Finish putting data in queue
- # Acquire the lock
- # with lock_state:
- # lock_state.acquire()
- # Add the cart-pose error
- queue_of_cart_pose_error.put(cart_pose_error)
- # Add the reference position and velocity
- # When the cart-MPC is called, make sure to linearly interpolate from the previous state to the new-state and send 10 values (20 Hz demand from a 2 Hz process).
- queue_of_ref_EE_states.put(ref_EE_state)
- # Release the lock
- # lock_state.release()
- # Wait till a set amount of time has elapsed
- # print(str(time.time()-self.start_time)+" Inside A: After")
- while((time.time()-self.start_time)<next_time_inst):
- continue
- # Update next-time
- next_time_inst = next_time_inst+self.delta_t_mpc
- def follow_new_EE_pose(self,
- queue_of_curr_state_qp_sol, # Queue of in-coming current system-state (for feedback | @ 20 Hz)
- queue_of_ref_EE_states, # Queue being sent to the QP-solver ( @ 20 Hz)
- queue_of_joint_vel, # Queue to be sent out as joint-velocities ( @ 20 Hz)
- lock_state): # Pass the lock-state that will control the sequence in which the locked-variables are accessed
- # This processes' job is to follow the demanded EE-state.
- # Next time-instant for giving jint-velocities
- next_time_inst = self.init_process_time+self.delta_t_ee
- # Wait till the off-setted time has elapsed
- while((time.time()-self.start_time)<self.init_process_time):
- continue
- while True:
- try:
- '''
- try to get new_vel from the `queue of ref EE states`. get_nowait() function will
- raise `queue.Empty` exception if the queue is empty.
- queue(False) function would do the same task aswell.
- '''
- # print(str(time.time()-self.start_time)+" Inside B_0: Before")
- # Finish getting data from queue
- # Reference EE state (from the cart-MPC)
- ref_EE_state = queue_of_ref_EE_states.get_nowait()
- # print("ref_EE_state")
- # System joint position (from ROS-topics)
- # Acquire the lock
- # with lock_state:
- # lock_state.acquire()
- # Wait for the data to be loaded
- curr_joint_pos = queue_of_curr_state_qp_sol.get()
- # print("curr_joint_pos")
- # Release the lock
- # lock_state.release()
- # print(str(time.time()-self.start_time)+" Inside B_0: After")
- except queue.Empty:
- # Need to import `queue` for this to work
- # Since no new velocities are in the queue, we will be brining the system to a stop.
- # New (terminal) joint velocity
- new_joint_vel = np.zeros((10,1))
- # print(str(time.time()-self.start_time)+" Inside B_2: Before")
- # Finish putting data in queue
- # Acquire the lock
- # with lock_state:
- # lock_state.acquire()
- # Send zero-velocity-command to robot.
- queue_of_joint_vel.put(new_joint_vel)
- # Release the lock
- # lock_state.release()
- # print(str(time.time()-self.start_time)+" Inside B_2: After")
- # Wait till a set amount of time has elapsed
- while((time.time()-self.start_time)<next_time_inst):
- continue
- # Update next-time
- # print(curr_joint_pos)
- break
- else:
- '''
- if no exception has been raised, add the task completion
- message to task_that_are_done queue
- '''
- # Reference EE position
- mat_w_G_10_ref = np.zeros((4,4))
- mat_w_G_10_ref[0,2] = -1
- mat_w_G_10_ref[1,1] = 1
- mat_w_G_10_ref[2,0] = 1
- mat_w_G_10_ref[0:3,3:4] = ref_EE_state[0:3,0:1]
- mat_w_G_10_ref[3,3] = 1
- # Reference FT wrench
- ft_wrench_ref = ref_EE_state[6:12,0:1]
- # Calculate new joint velocity
- # new_joint_vel = self.mob_man.run_QPSOL(curr_joint_pos,
- # mat_w_G_10_ref,
- # ref_EE_state[3:6,0:1],
- # ft_wrench_ref-curr_joint_pos[10:16,0])
- new_joint_vel = np.zeros((10,1))
- new_joint_vel[9,0] = next_time_inst
- # Re-orient the MB linear-velocity from world-frame to local-frame
- new_joint_vel[0:2,0] = np.matmul(np.array([[np.cos(curr_joint_pos[2,0]),-np.sin(curr_joint_pos[2,0])],
- [np.sin(curr_joint_pos[2,0]),np.cos(curr_joint_pos[2,0])]]),
- new_joint_vel[0:2,0])
- # print(str(time.time()-self.start_time)+" Inside B_1: Before")
- # Finish putting data in queue
- # Acquire the lock
- # with lock_state:
- # lock_state.acquire()
- # Send velocity-commands to Robot
- queue_of_joint_vel.put(new_joint_vel)
- # Release the lock
- # lock_state.release()
- # Wait till a set amount of time has elapsed
- # print(str(time.time()-self.start_time)+" Inside B_1: After")
- while((time.time()-self.start_time)<next_time_inst):
- continue
- # Update next-time
- next_time_inst = next_time_inst+self.delta_t_ee
- if __name__ == '__main__':
- # rospy.loginfo("Demanded BHand-pose-class initialising...")
- # rospy.init_node('cart_mpc_action_server')
- # rospy.loginfo(rospy.get_name() + ' start')
- # # Go to class functions that do all the heavy lifting. Do error checking.
- # try:
- # BHand_dem_pose()
- # rospy.spin()
- # except rospy.ROSInterruptException as e:
- # print(str(e))
- print("Starting test ...")
- follow_bhand_pose = BHand_dem_pose()
- follow_bhand_pose.goal_callback(goal=2)
Advertisement
Add Comment
Please, Sign In to add comment