|
|
@ -27,13 +27,8 @@ from nav_msgs.msg import Odometry |
|
|
|
from sensor_msgs.msg import JointState, Imu, MagneticField |
|
|
|
from std_msgs.msg import Header |
|
|
|
|
|
|
|
import hello_helpers.hello_misc as hm |
|
|
|
from hello_helpers.gripper_conversion import GripperConversion |
|
|
|
from joint_trajectory_server import JointTrajectoryAction |
|
|
|
|
|
|
|
GRIPPER_DEBUG = False |
|
|
|
BACKLASH_DEBUG = False |
|
|
|
|
|
|
|
|
|
|
|
class StretchBodyNode: |
|
|
|
|
|
|
@ -44,23 +39,6 @@ class StretchBodyNode: |
|
|
|
self.default_goal_timeout_s = 10.0 |
|
|
|
self.default_goal_timeout_duration = rospy.Duration(self.default_goal_timeout_s) |
|
|
|
|
|
|
|
# Initialize calibration offsets |
|
|
|
self.head_tilt_calibrated_offset_rad = 0.0 |
|
|
|
self.head_pan_calibrated_offset_rad = 0.0 |
|
|
|
|
|
|
|
# Initialize backlash state |
|
|
|
self.backlash_state = {'head_tilt_looking_up' : False, |
|
|
|
'head_pan_looked_left' : False, |
|
|
|
'wrist_extension_retracted' : False} |
|
|
|
|
|
|
|
# Initialize backlash offsets |
|
|
|
self.head_pan_calibrated_looked_left_offset_rad = 0.0 |
|
|
|
self.head_tilt_calibrated_looking_up_offset_rad = 0.0 |
|
|
|
self.wrist_extension_calibrated_retracted_offset_m = 0.0 |
|
|
|
self.head_tilt_backlash_transition_angle_rad = -0.4 |
|
|
|
|
|
|
|
self.gripper_conversion = GripperConversion() |
|
|
|
|
|
|
|
self.mobile_base_manipulation_origin = None #{'x':None, 'y':None, 'theta':None} |
|
|
|
|
|
|
|
self.robot_stop_lock = threading.Lock() |
|
|
@ -85,47 +63,33 @@ class StretchBodyNode: |
|
|
|
def command_mobile_base_velocity_and_publish_state(self): |
|
|
|
self.robot_mode_rwlock.acquire_read() |
|
|
|
|
|
|
|
if BACKLASH_DEBUG: |
|
|
|
print('***') |
|
|
|
print('self.backlash_state =', self.backlash_state) |
|
|
|
|
|
|
|
# set new mobile base velocities, if appropriate |
|
|
|
# check on thread safety for this with callback that sets velocity command values |
|
|
|
# set new mobile base velocities if available |
|
|
|
if self.robot_mode == 'navigation': |
|
|
|
time_since_last_twist = rospy.get_time() - self.last_twist_time |
|
|
|
if time_since_last_twist < self.timeout: |
|
|
|
self.robot.base.set_velocity(self.linear_velocity_mps, self.angular_velocity_radps) |
|
|
|
self.robot.push_command() |
|
|
|
else: |
|
|
|
# Too much information in general, although it could be blocked, since it's just INFO. |
|
|
|
# Watchdog timer stops motion if no communication within timeout |
|
|
|
self.robot.base.set_velocity(0.0, 0.0) |
|
|
|
self.robot.push_command() |
|
|
|
|
|
|
|
# get copy of the current robot status (uses lock held by the robot) |
|
|
|
robot_status = self.robot.get_status() |
|
|
|
|
|
|
|
|
|
|
|
# In the future, consider using time stamps from the robot's |
|
|
|
# TODO: In the future, consider using time stamps from the robot's |
|
|
|
# motor control boards and other boards. These would need to |
|
|
|
# be synchronized with the rospy clock. |
|
|
|
#robot_time = robot_status['timestamp_pc'] |
|
|
|
#rospy.loginfo('robot_time =', robot_time) |
|
|
|
#current_time = rospy.Time.from_sec(robot_time) |
|
|
|
|
|
|
|
current_time = rospy.Time.now() |
|
|
|
robot_status = self.robot.get_status() |
|
|
|
|
|
|
|
# obtain odometry |
|
|
|
# assign relevant base status to variables |
|
|
|
base_status = robot_status['base'] |
|
|
|
x = base_status['x'] |
|
|
|
x_raw = x |
|
|
|
y = base_status['y'] |
|
|
|
theta = base_status['theta'] |
|
|
|
q = tf_conversions.transformations.quaternion_from_euler(0, 0, theta) |
|
|
|
x_vel = base_status['x_vel'] |
|
|
|
x_vel_raw = x_vel |
|
|
|
y_vel = base_status['y_vel'] |
|
|
|
x_effort = base_status['effort'][0] |
|
|
|
x_effort_raw = x_effort |
|
|
|
theta_vel = base_status['theta_vel'] |
|
|
|
pose_time_s = base_status['pose_time_s'] |
|
|
|
|
|
|
@ -134,71 +98,7 @@ class StretchBodyNode: |
|
|
|
x_vel = 0.0 |
|
|
|
x_effort = 0.0 |
|
|
|
|
|
|
|
# assign relevant arm status to variables |
|
|
|
arm_status = robot_status['arm'] |
|
|
|
if self.backlash_state['wrist_extension_retracted']: |
|
|
|
arm_backlash_correction = self.wrist_extension_calibrated_retracted_offset_m |
|
|
|
else: |
|
|
|
arm_backlash_correction = 0.0 |
|
|
|
|
|
|
|
if BACKLASH_DEBUG: |
|
|
|
print('arm_backlash_correction =', arm_backlash_correction) |
|
|
|
pos_out = arm_status['pos'] + arm_backlash_correction |
|
|
|
vel_out = arm_status['vel'] |
|
|
|
eff_out = arm_status['motor']['effort'] |
|
|
|
|
|
|
|
lift_status = robot_status['lift'] |
|
|
|
pos_up = lift_status['pos'] |
|
|
|
vel_up = lift_status['vel'] |
|
|
|
eff_up = lift_status['motor']['effort'] |
|
|
|
|
|
|
|
if self.use_robotis_end_of_arm: |
|
|
|
# assign relevant wrist status to variables |
|
|
|
wrist_status = robot_status['end_of_arm']['wrist_yaw'] |
|
|
|
wrist_rad = wrist_status['pos'] |
|
|
|
wrist_vel = wrist_status['vel'] |
|
|
|
wrist_effort = wrist_status['effort'] |
|
|
|
|
|
|
|
# assign relevant gripper status to variables |
|
|
|
gripper_status = robot_status['end_of_arm']['stretch_gripper'] |
|
|
|
if GRIPPER_DEBUG: |
|
|
|
print('-----------------------') |
|
|
|
print('gripper_status[\'pos\'] =', gripper_status['pos']) |
|
|
|
print('gripper_status[\'pos_pct\'] =', gripper_status['pos_pct']) |
|
|
|
gripper_aperture_m, gripper_finger_rad, gripper_finger_effort, gripper_finger_vel = self.gripper_conversion.status_to_all(gripper_status) |
|
|
|
if GRIPPER_DEBUG: |
|
|
|
print('gripper_aperture_m =', gripper_aperture_m) |
|
|
|
print('gripper_finger_rad =', gripper_finger_rad) |
|
|
|
print('-----------------------') |
|
|
|
|
|
|
|
if self.use_robotis_head: |
|
|
|
# assign relevant head pan status to variables |
|
|
|
head_pan_status = robot_status['head']['head_pan'] |
|
|
|
if self.backlash_state['head_pan_looked_left']: |
|
|
|
pan_backlash_correction = self.head_pan_calibrated_looked_left_offset_rad |
|
|
|
else: |
|
|
|
pan_backlash_correction = 0.0 |
|
|
|
if BACKLASH_DEBUG: |
|
|
|
print('pan_backlash_correction =', pan_backlash_correction) |
|
|
|
head_pan_rad = head_pan_status['pos'] + self.head_pan_calibrated_offset_rad + pan_backlash_correction |
|
|
|
head_pan_vel = head_pan_status['vel'] |
|
|
|
head_pan_effort = head_pan_status['effort'] |
|
|
|
|
|
|
|
# assign relevant head tilt status to variables |
|
|
|
head_tilt_status = robot_status['head']['head_tilt'] |
|
|
|
if self.backlash_state['head_tilt_looking_up']: |
|
|
|
tilt_backlash_correction = self.head_tilt_calibrated_looking_up_offset_rad |
|
|
|
else: |
|
|
|
tilt_backlash_correction = 0.0 |
|
|
|
if BACKLASH_DEBUG: |
|
|
|
print('tilt_backlash_correction =', tilt_backlash_correction) |
|
|
|
head_tilt_rad = head_tilt_status['pos'] + self.head_tilt_calibrated_offset_rad + tilt_backlash_correction |
|
|
|
head_tilt_vel = head_tilt_status['vel'] |
|
|
|
head_tilt_effort = head_tilt_status['effort'] |
|
|
|
|
|
|
|
q = tf_conversions.transformations.quaternion_from_euler(0, 0, theta) |
|
|
|
|
|
|
|
if self.broadcast_odom_tf: |
|
|
|
if self.broadcast_odom_tf: |
|
|
|
# publish odometry via TF |
|
|
|
t = TransformStamped() |
|
|
|
t.header.stamp = current_time |
|
|
@ -213,7 +113,7 @@ class StretchBodyNode: |
|
|
|
t.transform.rotation.w = q[3] |
|
|
|
self.tf_broadcaster.sendTransform(t) |
|
|
|
|
|
|
|
# publish odometry via the odom topic |
|
|
|
# publish odometry |
|
|
|
odom = Odometry() |
|
|
|
odom.header.stamp = current_time |
|
|
|
odom.header.frame_id = self.odom_frame_id |
|
|
@ -228,80 +128,36 @@ class StretchBodyNode: |
|
|
|
odom.twist.twist.angular.z = theta_vel |
|
|
|
self.odom_pub.publish(odom) |
|
|
|
|
|
|
|
# publish joint state for the arm |
|
|
|
# publish joint state |
|
|
|
joint_state = JointState() |
|
|
|
joint_state.header.stamp = current_time |
|
|
|
# joint_arm_l3 is the most proximal and joint_arm_l0 is the |
|
|
|
# most distal joint of the telescoping arm model. The joints |
|
|
|
# are connected in series such that moving the most proximal |
|
|
|
# joint moves all the other joints in the global frame. |
|
|
|
joint_state.name = ['wrist_extension', 'joint_lift', 'joint_arm_l3', 'joint_arm_l2', 'joint_arm_l1', 'joint_arm_l0'] |
|
|
|
|
|
|
|
# set positions of the telescoping joints |
|
|
|
positions = [pos_out/4.0 for i in xrange(4)] |
|
|
|
# set lift position |
|
|
|
positions.insert(0, pos_up) |
|
|
|
# set wrist_extension position |
|
|
|
positions.insert(0, pos_out) |
|
|
|
|
|
|
|
# set velocities of the telescoping joints |
|
|
|
velocities = [vel_out/4.0 for i in xrange(4)] |
|
|
|
# set lift velocity |
|
|
|
velocities.insert(0, vel_up) |
|
|
|
# set wrist_extension velocity |
|
|
|
velocities.insert(0, vel_out) |
|
|
|
|
|
|
|
# set efforts of the telescoping joints |
|
|
|
efforts = [eff_out for i in xrange(4)] |
|
|
|
# set lift effort |
|
|
|
efforts.insert(0, eff_up) |
|
|
|
# set wrist_extension effort |
|
|
|
efforts.insert(0, eff_out) |
|
|
|
|
|
|
|
if self.use_robotis_head: |
|
|
|
head_joint_names = ['joint_head_pan', 'joint_head_tilt'] |
|
|
|
joint_state.name.extend(head_joint_names) |
|
|
|
|
|
|
|
positions.append(head_pan_rad) |
|
|
|
velocities.append(head_pan_vel) |
|
|
|
efforts.append(head_pan_effort) |
|
|
|
|
|
|
|
positions.append(head_tilt_rad) |
|
|
|
velocities.append(head_tilt_vel) |
|
|
|
efforts.append(head_tilt_effort) |
|
|
|
|
|
|
|
if self.use_robotis_end_of_arm: |
|
|
|
end_of_arm_joint_names = ['joint_wrist_yaw', 'joint_gripper_finger_left', 'joint_gripper_finger_right'] |
|
|
|
joint_state.name.extend(end_of_arm_joint_names) |
|
|
|
|
|
|
|
positions.append(wrist_rad) |
|
|
|
velocities.append(wrist_vel) |
|
|
|
efforts.append(wrist_effort) |
|
|
|
|
|
|
|
positions.append(gripper_finger_rad) |
|
|
|
velocities.append(gripper_finger_vel) |
|
|
|
efforts.append(gripper_finger_effort) |
|
|
|
|
|
|
|
positions.append(gripper_finger_rad) |
|
|
|
velocities.append(gripper_finger_vel) |
|
|
|
efforts.append(gripper_finger_effort) |
|
|
|
|
|
|
|
# set virtual joint for mobile base translation |
|
|
|
joint_state.name.append('joint_mobile_base_translation') |
|
|
|
if self.robot_mode == 'manipulation': |
|
|
|
manipulation_translation = x_raw - self.mobile_base_manipulation_origin['x'] |
|
|
|
positions.append(manipulation_translation) |
|
|
|
velocities.append(x_vel_raw) |
|
|
|
efforts.append(x_effort_raw) |
|
|
|
else: |
|
|
|
positions.append(0.0) |
|
|
|
velocities.append(0.0) |
|
|
|
efforts.append(0.0) |
|
|
|
|
|
|
|
# set joint_state |
|
|
|
joint_state.position = positions |
|
|
|
joint_state.velocity = velocities |
|
|
|
joint_state.effort = efforts |
|
|
|
for cg in self.joint_trajectory_action.command_groups: |
|
|
|
pos, vel, effort = cg.joint_state(robot_status, robot_mode=self.robot_mode, |
|
|
|
manipulation_origin=self.mobile_base_manipulation_origin) |
|
|
|
joint_state.name.append(cg.name) |
|
|
|
joint_state.position.append(pos) |
|
|
|
joint_state.velocity.append(vel) |
|
|
|
joint_state.effort.append(effort) |
|
|
|
|
|
|
|
# add telescoping joints to joint state |
|
|
|
arm_cg = self.joint_trajectory_action.arm_cg |
|
|
|
joint_state.name.extend(arm_cg.telescoping_joints) |
|
|
|
pos, vel, effort = arm_cg.joint_state(robot_status) |
|
|
|
for _ in range(len(arm_cg.telescoping_joints)): |
|
|
|
joint_state.position.append(pos / len(arm_cg.telescoping_joints)) |
|
|
|
joint_state.velocity.append(vel / len(arm_cg.telescoping_joints)) |
|
|
|
joint_state.effort.append(effort) |
|
|
|
|
|
|
|
# add gripper joints to joint state |
|
|
|
gripper_cg = self.joint_trajectory_action.gripper_cg |
|
|
|
missing_gripper_joint_names = list(set(gripper_cg.gripper_joint_names) - set(joint_state.name)) |
|
|
|
for j in missing_gripper_joint_names: |
|
|
|
pos, vel, effort = gripper_cg.joint_state(robot_status, joint_name=j) |
|
|
|
joint_state.name.append(j) |
|
|
|
joint_state.position.append(pos) |
|
|
|
joint_state.velocity.append(vel) |
|
|
|
joint_state.effort.append(effort) |
|
|
|
|
|
|
|
self.joint_state_pub.publish(joint_state) |
|
|
|
|
|
|
|
################################################## |
|
|
@ -323,7 +179,6 @@ class StretchBodyNode: |
|
|
|
i.angular_velocity.x = gx |
|
|
|
i.angular_velocity.y = gy |
|
|
|
i.angular_velocity.z = gz |
|
|
|
|
|
|
|
i.linear_acceleration.x = ax |
|
|
|
i.linear_acceleration.y = ay |
|
|
|
i.linear_acceleration.z = az |
|
|
@ -508,47 +363,40 @@ class StretchBodyNode: |
|
|
|
if self.broadcast_odom_tf: |
|
|
|
self.tf_broadcaster = tf2_ros.TransformBroadcaster() |
|
|
|
|
|
|
|
large_ang = 45.0 * np.pi/180.0 |
|
|
|
|
|
|
|
large_ang = np.radians(45.0) |
|
|
|
filename = rospy.get_param('~controller_calibration_file') |
|
|
|
rospy.loginfo('Loading controller calibration parameters for the head from YAML file named {0}'.format(filename)) |
|
|
|
with open(filename, 'r') as fid: |
|
|
|
controller_parameters = yaml.safe_load(fid) |
|
|
|
|
|
|
|
rospy.loginfo('controller parameters loaded = {0}'.format(controller_parameters)) |
|
|
|
|
|
|
|
self.head_tilt_calibrated_offset_rad = controller_parameters['tilt_angle_offset'] |
|
|
|
ang = self.head_tilt_calibrated_offset_rad |
|
|
|
if (abs(ang) > large_ang): |
|
|
|
rospy.loginfo('WARNING: self.head_tilt_calibrated_offset_rad HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('self.head_tilt_calibrated_offset_rad in degrees = {0}'.format(np.degrees(self.head_tilt_calibrated_offset_rad))) |
|
|
|
|
|
|
|
self.head_pan_calibrated_offset_rad = controller_parameters['pan_angle_offset'] |
|
|
|
ang = self.head_pan_calibrated_offset_rad |
|
|
|
if (abs(ang) > large_ang): |
|
|
|
rospy.loginfo('WARNING: self.head_pan_calibrated_offset_rad HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('self.head_pan_calibrated_offset_rad in degrees = {0}'.format(np.degrees(self.head_pan_calibrated_offset_rad))) |
|
|
|
|
|
|
|
self.head_pan_calibrated_looked_left_offset_rad = controller_parameters['pan_looked_left_offset'] |
|
|
|
ang = self.head_pan_calibrated_looked_left_offset_rad |
|
|
|
if (abs(ang) > large_ang): |
|
|
|
rospy.loginfo('WARNING: self.head_pan_calibrated_looked_left_offset_rad HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('self.head_pan_calibrated_looked_left_offset_rad in degrees = {0}'.format(np.degrees(self.head_pan_calibrated_looked_left_offset_rad))) |
|
|
|
|
|
|
|
self.head_tilt_backlash_transition_angle_rad = controller_parameters['tilt_angle_backlash_transition'] |
|
|
|
rospy.loginfo('self.head_tilt_backlash_transition_angle_rad in degrees = {0}'.format(np.degrees(self.head_tilt_backlash_transition_angle_rad))) |
|
|
|
|
|
|
|
self.head_tilt_calibrated_looking_up_offset_rad = controller_parameters['tilt_looking_up_offset'] |
|
|
|
ang = self.head_tilt_calibrated_looking_up_offset_rad |
|
|
|
if (abs(ang) > large_ang): |
|
|
|
rospy.loginfo('WARNING: self.head_tilt_calibrated_looking_up_offset_rad HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('self.head_tilt_calibrated_looking_up_offset_rad in degrees = {0}'.format(np.degrees(self.head_tilt_calibrated_looking_up_offset_rad))) |
|
|
|
|
|
|
|
self.wrist_extension_calibrated_retracted_offset_m = controller_parameters['arm_retracted_offset'] |
|
|
|
m = self.wrist_extension_calibrated_retracted_offset_m |
|
|
|
if (abs(m) > 0.05): |
|
|
|
rospy.loginfo('WARNING: self.wrist_extension_calibrated_retracted_offset_m HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('self.wrist_extension_calibrated_retracted_offset_m in meters = {0}'.format(self.wrist_extension_calibrated_retracted_offset_m)) |
|
|
|
self.controller_parameters = yaml.safe_load(fid) |
|
|
|
rospy.loginfo('controller parameters loaded = {0}'.format(self.controller_parameters)) |
|
|
|
|
|
|
|
head_tilt_calibrated_offset_rad = self.controller_parameters['tilt_angle_offset'] |
|
|
|
if (abs(head_tilt_calibrated_offset_rad) > large_ang): |
|
|
|
rospy.logwarn('WARNING: head_tilt_calibrated_offset_rad HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('head_tilt_calibrated_offset_rad in degrees = {0}'.format(np.degrees(head_tilt_calibrated_offset_rad))) |
|
|
|
|
|
|
|
head_pan_calibrated_offset_rad = self.controller_parameters['pan_angle_offset'] |
|
|
|
if (abs(head_pan_calibrated_offset_rad) > large_ang): |
|
|
|
rospy.logwarn('WARNING: head_pan_calibrated_offset_rad HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('head_pan_calibrated_offset_rad in degrees = {0}'.format(np.degrees(head_pan_calibrated_offset_rad))) |
|
|
|
|
|
|
|
head_pan_calibrated_looked_left_offset_rad = self.controller_parameters['pan_looked_left_offset'] |
|
|
|
if (abs(head_pan_calibrated_looked_left_offset_rad) > large_ang): |
|
|
|
rospy.logwarn('WARNING: head_pan_calibrated_looked_left_offset_rad HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('head_pan_calibrated_looked_left_offset_rad in degrees = {0}'.format(np.degrees(head_pan_calibrated_looked_left_offset_rad))) |
|
|
|
|
|
|
|
head_tilt_backlash_transition_angle_rad = self.controller_parameters['tilt_angle_backlash_transition'] |
|
|
|
rospy.loginfo('head_tilt_backlash_transition_angle_rad in degrees = {0}'.format(np.degrees(head_tilt_backlash_transition_angle_rad))) |
|
|
|
|
|
|
|
head_tilt_calibrated_looking_up_offset_rad = self.controller_parameters['tilt_looking_up_offset'] |
|
|
|
if (abs(head_tilt_calibrated_looking_up_offset_rad) > large_ang): |
|
|
|
rospy.logwarn('WARNING: head_tilt_calibrated_looking_up_offset_rad HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('head_tilt_calibrated_looking_up_offset_rad in degrees = {0}'.format(np.degrees(head_tilt_calibrated_looking_up_offset_rad))) |
|
|
|
|
|
|
|
arm_calibrated_retracted_offset_m = self.controller_parameters['arm_retracted_offset'] |
|
|
|
if (abs(arm_calibrated_retracted_offset_m) > 0.05): |
|
|
|
rospy.logwarn('WARNING: arm_calibrated_retracted_offset_m HAS AN UNUSUALLY LARGE MAGNITUDE') |
|
|
|
rospy.loginfo('arm_calibrated_retracted_offset_m in meters = {0}'.format(arm_calibrated_retracted_offset_m)) |
|
|
|
|
|
|
|
self.linear_velocity_mps = 0.0 # m/s ROS SI standard for cmd_vel (REP 103) |
|
|
|
self.angular_velocity_radps = 0.0 # rad/s ROS SI standard for cmd_vel (REP 103) |
|
|
|