Browse Source

Testrig Data Collection node working,Integrated TestRig option to CollectHeadCalibrationDataNode

feature/d435i_testrig
Mohamed Fazil 2 years ago
parent
commit
0de99eb046
8 changed files with 22323 additions and 0 deletions
  1. +59
    -0
      stretch_camera_testrig/config/testrig_marker_info.yaml
  2. +3392
    -0
      stretch_camera_testrig/data/testrig_collected_data_202202161602.yaml
  3. +17648
    -0
      stretch_camera_testrig/data/testrig_collected_data_202202161605.yaml
  4. +33
    -0
      stretch_camera_testrig/launch/test_basic.launch
  5. +0
    -0
     
  6. +820
    -0
      stretch_camera_testrig/nodes/collect_head_calibration_data.py
  7. +38
    -0
      stretch_camera_testrig/nodes/test_basic.py
  8. +333
    -0
      stretch_camera_testrig/rviz/rviz.rviz

+ 59
- 0
stretch_camera_testrig/config/testrig_marker_info.yaml View File

@ -0,0 +1,59 @@
# This file contains the nominal poses of the each Aruco markers
# that is to be used in the test rig in Millimeters. Each aruco marker's
# pose(mm)/orientation(rad) are to be measured and inserted carefully.
'testrig_aruco_marker_info':
'base_left':
'pose':
'x': 0
'y': 0
'z': 0
'orientation':
'x': 0
'y': 0
'z': 0
'w': 0
'base_right':
'pose':
'x': 0
'y': 0
'z': 0
'orientation':
'x': 0
'y': 0
'z': 0
'w': 0
'shoulder_top':
'pose':
'x': 0
'y': 0
'z': 0
'orientation':
'x': 0
'y': 0
'z': 0
'w': 0
'wrist_top':
'pose':
'x': 0
'y': 0
'z': 0
'orientation':
'x': 0
'y': 0
'z': 0
'w': 0
'wrist_inside':
'pose':
'x': 0
'y': 0
'z': 0
'orientation':
'x': 0
'y': 0
'z': 0
'w': 0

+ 3392
- 0
stretch_camera_testrig/data/testrig_collected_data_202202161602.yaml
File diff suppressed because it is too large
View File


+ 17648
- 0
stretch_camera_testrig/data/testrig_collected_data_202202161605.yaml
File diff suppressed because it is too large
View File


+ 33
- 0
stretch_camera_testrig/launch/test_basic.launch View File

@ -0,0 +1,33 @@
<launch>
<!-- REALSENSE D435i -->
<include file="$(find stretch_core)/launch/d435i_basic.launch" pass_all_args="true">
<!--
HIGHEST RESOLUTION, but also has the highest minimum depth
(280mm Min-Z) below which objects generate bad noise, such as
when the arm and gripper are raised close to the camera.
-->
<arg name="depth_width" value="1280"/>
<arg name="depth_height" value="720"/>
<arg name="color_width" value="1280"/>
<arg name="color_height" value="720"/>
</include>
<!-- REALSENSE D435i Model Load -->
<param name="robot_description" command="$(find xacro)/xacro --inorder '$(find realsense2_description)/urdf/test_d435i_camera.urdf.xacro' use_nominal_extrinsics:=true" />
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" />
<!-- RVIZ Visualization -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find stretch_camera_testrig)/rviz/rviz.rviz"/>
<!-- ARUCO MARKER DETECTOR -->
<rosparam command="load" file="$(find stretch_core)/config/stretch_marker_dict.yaml" />
<node name="detect_aruco_markers" pkg="stretch_core" type="detect_aruco_markers" output="screen"/>
<!-- ARUCO Marker Actual Poses in Testrig -->
<rosparam command="load" file="$(find stretch_camera_testrig)/config/testrig_marker_info.yaml" />
<!-- Test Rig Node -->
<!-- <node name="test_rig_node" pkg="stretch_camera_testrig" type="test_basic.py" output="screen"/> -->
</launch>

+ 0
- 0
View File


+ 820
- 0
stretch_camera_testrig/nodes/collect_head_calibration_data.py View File

@ -0,0 +1,820 @@
#!/usr/bin/env python
from __future__ import print_function
from sensor_msgs.msg import JointState
from geometry_msgs.msg import Twist
from nav_msgs.msg import Odometry
import rospy
import actionlib
from control_msgs.msg import FollowJointTrajectoryAction
from control_msgs.msg import FollowJointTrajectoryGoal
from trajectory_msgs.msg import JointTrajectoryPoint
import message_filters
from visualization_msgs.msg import Marker
from visualization_msgs.msg import MarkerArray
from tf.transformations import quaternion_from_euler, euler_from_quaternion, quaternion_from_matrix
from sensor_msgs.msg import Imu
import math
import time
import threading
import sys
import argparse as ap
import numpy as np
import ros_numpy
from copy import deepcopy
import yaml
import time
class CollectHeadCalibrationDataNode:
def __init__(self,test_rig=False):
self.rate = 10.0
self.joint_state = None
self.acceleration = None
self.data_time = None
self.marker_time = None
self.test_rig = test_rig
self.data_lock = threading.Lock()
if test_rig:
self.number_of_testrig_samples=40
def calibration_data_callback(self, accel, marker_array, joint_state=None):
# Prepare and assign data for calibration to member
# variables. The configuration of the robot's joints, the
# D435i acceleration, and the 3D fiducial estimates made via
# D435i imagery are expected to be synchronized for this
# callback.
with self.data_lock:
if not self.test_rig:
self.data_time = joint_state.header.stamp
self.joint_state = joint_state
self.acceleration = [accel.linear_acceleration.x, accel.linear_acceleration.y, accel.linear_acceleration.z]
self.wrist_inside_marker_pose = None
self.wrist_top_marker_pose = None
self.base_left_marker_pose = None
self.base_right_marker_pose = None
self.shoulder_marker_pose = None
self.marker_time = None
for marker in marker_array.markers:
# set marker_time to the earliest marker time
if self.marker_time is None:
self.marker_time = marker.header.stamp
elif marker.header.stamp < self.marker_time:
self.marker_time = marker.header.stamp
if marker.id == self.wrist_inside_marker_id:
self.wrist_inside_marker_pose = marker.pose
if marker.id == self.wrist_top_marker_id:
self.wrist_top_marker_pose = marker.pose
if marker.id == self.base_left_marker_id:
self.base_left_marker_pose = marker.pose
if marker.id == self.base_right_marker_id:
self.base_right_marker_pose = marker.pose
if marker.id == self.shoulder_marker_id:
self.shoulder_marker_pose = marker.pose
def move_to_pose(self, pose):
# Prepare and send a goal pose to which the robot should move.
joint_names = [key for key in pose]
self.trajectory_goal.trajectory.joint_names = joint_names
joint_positions = [pose[key] for key in joint_names]
self.point.positions = joint_positions
self.trajectory_goal.trajectory.points = [self.point]
self.trajectory_goal.trajectory.header.stamp = rospy.Time.now()
self.trajectory_client.send_goal(self.trajectory_goal)
self.trajectory_client.wait_for_result()
def get_samples(self, pan_angle_center, tilt_angle_center, wrist_extension_center, number_of_samples, first_move=False):
# Collect N observations (samples) centered around the
# provided head and wrist extension pose. If first_move is
# true, it indicates that this is the first movement to a new
# region for calibration, which can induce more vibrations and
# thus benefit from more time to settle prior to collecting a
# sample.
head_motion_settle_time = 1.0 #5.0
first_pan_tilt_extra_settle_time = 1.0
wait_before_each_sample_s = 0.2
# generate pan and wrist backlash samples
# tilt backlash
tilt_backlash_transition = self.tilt_angle_backlash_transition_rad
# Ensure that the head is clearly in one of the two head tilt
# backlash regimes and not in the indeterminate transition
# region.
tilt_backlash_safety_margin = 0.08
tilt_backlash_looking_down_max = tilt_backlash_transition - tilt_backlash_safety_margin # looking down
tilt_backlash_looking_up_min = tilt_backlash_transition + tilt_backlash_safety_margin # looking up
tilt_angle = tilt_angle_center
if (tilt_angle_center > tilt_backlash_looking_down_max) and (tilt_angle_center < tilt_backlash_looking_up_min):
# Commanded tilt_angle_center is in an uncertain backlash range
# of tilt, so change it to be in a confident range.
down_diff = abs(tilt_backlash_looking_down_max - tilt_angle_center)
up_diff = abs(tilt_backlash_looking_up_min - tilt_angle_center)
if up_diff < down_diff:
tilt_angle = tilt_backlash_looking_up_min
else:
tilt_angle = tilt_backlash_looking_down_max
if tilt_angle > tilt_backlash_transition:
joint_head_tilt_looking_up = True
else:
joint_head_tilt_looking_up = False
# represent all head pan and wrist extension backlash states
backlash_combinations = ((False, False), (False, True), (True, True), (True, False))
pan_backlash_change_rad = 0.07 # ~8 degrees
wrist_backlash_change_m = 0.01 # 1 cm
samples = []
# collect samples for all head pan and wrist extension
# backlash states
for joint_head_pan_looked_left, wrist_extension_retracted in backlash_combinations:
if joint_head_pan_looked_left:
pan_angle = pan_angle_center + pan_backlash_change_rad
else:
pan_angle = pan_angle_center - pan_backlash_change_rad
if wrist_extension_retracted:
wrist_extension = wrist_extension_center - wrist_backlash_change_m
else:
wrist_extension = wrist_extension_center + wrist_backlash_change_m
# move to backlash state
head_arm_pose = {'joint_head_pan': pan_angle,
'joint_head_tilt': tilt_angle,
'wrist_extension': wrist_extension}
self.move_to_pose(head_arm_pose)
# wait for the joints and sensors to settle
rospy.sleep(head_motion_settle_time)
settled_time = rospy.Time.now()
# The first move to a pose is typically larger and can
# benefit from more settling time due to induced
# vibrations.
if first_move:
rospy.sleep(first_pan_tilt_extra_settle_time)
for sample_number in range(number_of_samples):
rospy.sleep(wait_before_each_sample_s)
observation = {'joints': {'joint_head_pan': None,
'joint_head_tilt': None,
'wrist_extension' : None,
'joint_lift': None },
'backlash_state': {
'joint_head_pan_looked_left': None,
'joint_head_tilt_looking_up': None,
'wrist_extension_retracted': None
},
'camera_measurements': { 'd435i_acceleration': None,
'wrist_inside_marker_pose': None,
'wrist_top_marker_pose': None,
'base_left_marker_pose': None,
'base_right_marker_pose': None,
'shoulder_marker_pose': None}
}
# wait for a sample taken after the robot has
# settled
timeout_duration = rospy.Duration(secs=10)
start_wait = rospy.Time.now()
timeout = False
data_ready = False
while (not data_ready) and (not timeout):
# check the timing of the current sample
with self.data_lock:
if (self.data_time is not None) and (self.data_time > settled_time):
if (self.marker_time is None):
# joint_states and acceleration
# were taken after the robot
# settled and there are no aruco
# marker poses
data_ready = True
elif (self.marker_time > settled_time):
# joint_states, acceleration,
# and aruco marker poses were
# taken after the robot
# settled
data_ready = True
if not data_ready:
#rospy.sleep(0.2) #0.1
rospy.sleep(1.0)
timeout = (rospy.Time.now() - start_wait) > timeout_duration
if timeout:
rospy.logerr('collect_head_calibration_data get_samples: timeout while waiting for sample.')
raise Exception('Timed out waiting for joint_states/accelerations/markers messages after 10 seconds')
with self.data_lock:
#set backlash joint state
backlash_state = observation['backlash_state']
backlash_state['joint_head_pan_looked_left'] = joint_head_pan_looked_left
backlash_state['joint_head_tilt_looking_up'] = joint_head_tilt_looking_up
backlash_state['wrist_extension_retracted'] = wrist_extension_retracted
# record the settled joint values
joints = observation['joints']
for joint_name in joints:
print('Joint name %s'%joint_name)
joint_index = self.joint_state.name.index(joint_name)
joint_value = self.joint_state.position[joint_index]
joints[joint_name] = joint_value
# record the settled camera measurments
camera_measurements = observation['camera_measurements']
camera_measurements['d435i_acceleration'] = self.acceleration
# record the settled aruco measurements
if self.wrist_inside_marker_pose is not None:
camera_measurements['wrist_inside_marker_pose'] = ros_numpy.numpify(self.wrist_inside_marker_pose).tolist()
if self.wrist_top_marker_pose is not None:
camera_measurements['wrist_top_marker_pose'] = ros_numpy.numpify(self.wrist_top_marker_pose).tolist()
if self.base_left_marker_pose is not None:
camera_measurements['base_left_marker_pose'] = ros_numpy.numpify(self.base_left_marker_pose).tolist()
if self.base_right_marker_pose is not None:
camera_measurements['base_right_marker_pose'] = ros_numpy.numpify(self.base_right_marker_pose).tolist()
if self.shoulder_marker_pose is not None:
camera_measurements['shoulder_marker_pose'] = ros_numpy.numpify(self.shoulder_marker_pose).tolist()
samples.append(observation)
return samples
def get_samples_testrig(self, number_of_samples):
# Collect N observations (samples) centered around the
# provided head and wrist extension pose. If first_move is
# true, it indicates that this is the first movement to a new
# region for calibration, which can induce more vibrations and
# thus benefit from more time to settle prior to collecting a
# sample.
samples = []
wait_before_each_sample_s = 0.2
for sample_number in range(number_of_samples):
rospy.sleep(wait_before_each_sample_s)
observation = {'joints': {'joint_head_pan': None,
'joint_head_tilt': None,
'wrist_extension' : None,
'joint_lift': None },
'backlash_state': {
'joint_head_pan_looked_left': None,
'joint_head_tilt_looking_up': None,
'wrist_extension_retracted': None
},
'camera_measurements': { 'd435i_acceleration': None,
'wrist_inside_marker_pose': None,
'wrist_top_marker_pose': None,
'base_left_marker_pose': None,
'base_right_marker_pose': None,
'shoulder_marker_pose': None}
}
# wait for a sample taken after the robot has
# settled
# timeout_duration = rospy.Duration(secs=10)
# start_wait = rospy.Time.now()
# timeout = False
# data_ready = False
# while (not data_ready) and (not timeout):
# # check the timing of the current sample
# with self.data_lock:
# if (self.data_time is not None) and (self.data_time > settled_time):
# if (self.marker_time is None):
# # joint_states and acceleration
# # were taken after the robot
# # settled and there are no aruco
# # marker poses
# data_ready = True
# elif (self.marker_time > settled_time):
# # joint_states, acceleration,
# # and aruco marker poses were
# # taken after the robot
# # settled
# data_ready = True
# if not data_ready:
# #rospy.sleep(0.2) #0.1
# rospy.sleep(1.0)
# timeout = (rospy.Time.now() - start_wait) > timeout_duration
# if timeout:
# rospy.logerr('collect_head_calibration_data get_samples: timeout while waiting for sample.')
# raise Exception('Timed out waiting for joint_states/accelerations/markers messages after 10 seconds')
with self.data_lock:
# record the settled camera measurments
camera_measurements = observation['camera_measurements']
camera_measurements['d435i_acceleration'] = self.acceleration
# record the settled aruco measurements
if self.wrist_inside_marker_pose is not None:
camera_measurements['wrist_inside_marker_pose'] = ros_numpy.numpify(self.wrist_inside_marker_pose).tolist()
if self.wrist_top_marker_pose is not None:
camera_measurements['wrist_top_marker_pose'] = ros_numpy.numpify(self.wrist_top_marker_pose).tolist()
if self.base_left_marker_pose is not None:
camera_measurements['base_left_marker_pose'] = ros_numpy.numpify(self.base_left_marker_pose).tolist()
if self.base_right_marker_pose is not None:
camera_measurements['base_right_marker_pose'] = ros_numpy.numpify(self.base_right_marker_pose).tolist()
if self.shoulder_marker_pose is not None:
camera_measurements['shoulder_marker_pose'] = ros_numpy.numpify(self.shoulder_marker_pose).tolist()
samples.append(observation)
return samples
def calibrate_pan_and_tilt(self, collect_check_data=False):
# Collects observations of fiducial markers on the robot's
# body while moving the head pan, head tilt, arm lift, and arm
# extension. There are five markers in total with the
# following locations: front left of the mobile base, front
# right of the mobile base, top of the shoulder, top of the
# wrist, and inside the wrist.
#
# When collect_check_data is True, fewer samples are collected
# with the intention of using them to check the current
# calibration. When collect_check_data is False, more samples
# are collected with the intention of fully calibrating the
# robot.
calibration_data = []
number_of_samples_per_head_pose = 1 #3
wrist_motion_settle_time = 1.0
########################################
##
## COLLECT GLOBAL HEAD LOOKING DOWN DATA
##
########################################
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('COLLECT GLOBAL HEAD LOOKING DOWN DATA')
rospy.loginfo('*************************************')
rospy.loginfo('')
# looking down data
min_tilt_angle_rad = -1.4
max_tilt_angle_rad = -0.5
min_pan_angle_rad = -3.8
max_pan_angle_rad = 1.3
if collect_check_data:
number_of_tilt_steps = 1
number_of_pan_steps = 3
else:
number_of_tilt_steps = 3 #4
number_of_pan_steps = 5 #7
pan_angles_rad = np.linspace(min_pan_angle_rad, max_pan_angle_rad, number_of_pan_steps)
tilt_angles_rad = np.linspace(min_tilt_angle_rad, max_tilt_angle_rad, number_of_tilt_steps)
rospy.loginfo('Move to a new arm pose.')
wrist_extension = 0.12
wrist_pose = {'wrist_extension': wrist_extension}
self.move_to_pose(wrist_pose)
lift_m = 0.16
lift_pose = {'joint_lift': lift_m}
self.move_to_pose(lift_pose)
# wait for the joints and sensors to settle
rospy.loginfo('Wait {0} seconds for the robot to settle after motion of the arm.'.format(wrist_motion_settle_time))
rospy.sleep(wrist_motion_settle_time)
rospy.loginfo('Starting to collect global head looking down data (expect to collect {0} samples).'.format(number_of_tilt_steps * number_of_pan_steps))
first_pan_tilt = True
for pan_angle in pan_angles_rad:
for tilt_angle in tilt_angles_rad:
observation = self.get_samples(pan_angle, tilt_angle, wrist_extension, number_of_samples_per_head_pose, first_move=first_pan_tilt)
first_pan_tilt = False
calibration_data.extend(observation)
n = len(calibration_data)
if (n % 10) == 0:
rospy.loginfo('{0} samples collected so far.'.format(n))
#######################################
##
## COLLECT GLOBAL HEAD LOOKING UP DATA
##
#######################################
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('COLLECT GLOBAL HEAD LOOKING UP DATA')
rospy.loginfo('*************************************')
rospy.loginfo('')
if not collect_check_data:
tilt_angles_rad = [-0.3, 0.38]
pan_angles_rad = [-3.8, -1.66, 1.33]
rospy.loginfo('Move to a new arm pose.')
lift_m = 0.92
lift_pose = {'joint_lift': lift_m}
self.move_to_pose(lift_pose)
wrist_extension = 0.29
wrist_pose = {'wrist_extension': wrist_extension}
self.move_to_pose(wrist_pose)
# wait for the joints and sensors to settle
rospy.loginfo('Wait {0} seconds for the robot to settle after motion of the arm.'.format(wrist_motion_settle_time))
rospy.sleep(wrist_motion_settle_time)
rospy.loginfo('Starting to collect global head looking up data (expect to collect {0} samples).'.format(len(pan_angles_rad) * len(tilt_angles_rad)))
first_pan_tilt = True
for pan_angle in pan_angles_rad:
for tilt_angle in tilt_angles_rad:
observation = self.get_samples(pan_angle, tilt_angle, wrist_extension, number_of_samples_per_head_pose, first_move=first_pan_tilt)
first_pan_tilt = False
calibration_data.extend(observation)
n = len(calibration_data)
if (n % 10) == 0:
rospy.loginfo('{0} samples collected so far.'.format(n))
#######################################
##
## COLLECT HIGH SHOULDER DATA
##
#######################################
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('COLLECT HIGH SHOULDER DATA')
rospy.loginfo('*************************************')
rospy.loginfo('')
##############################################################
### poses that see the shoulder marker
#
# lift 0.8
# pan -3.82 to -3.619
# tilt -0.918 to -0.49
#
# lift 0.2
# pan -3.85 to -2.48
# tilt -1.23 to -0.87
#
# two poses with good coverage
# (needs to account for +/- 0.07 rad for pan backlash)
# pan -3.7, tilt -0.7
# pan -3.7, tilt -1.1
#
# Marker detected with point clouds
# lift 0.5, pan -3.7, tilt = -1.4 to -1.2
# lift 0.65, pan -3.85, tilt = -1.0
# lift 0.74, pan -3.85, tilt = -0.78 to -1.4 (-1.4 also sees the base markers)
##############################################################
wrist_extension = 0.29
# low shoulder height
pan_angle_rad = -3.7
tilt_angle_rad_1 = -1.4
tilt_angle_rad_2 = -1.2
lift_m = 0.5
pose = {'joint_lift': lift_m}
rospy.loginfo('Move to first shoulder capture height.')
self.move_to_pose(pose)
rospy.loginfo('Wait {0} seconds for the robot to settle after motion of the arm.'.format(wrist_motion_settle_time))
rospy.sleep(wrist_motion_settle_time)
first_pan_tilt = True
observation = self.get_samples(pan_angle_rad, tilt_angle_rad_1, wrist_extension, number_of_samples_per_head_pose, first_move=first_pan_tilt)
calibration_data.extend(observation)
if not collect_check_data:
first_pan_tilt = False
observation = self.get_samples(pan_angle_rad, tilt_angle_rad_2, wrist_extension, number_of_samples_per_head_pose, first_move=first_pan_tilt)
calibration_data.extend(observation)
# high shoulder height
pan_angle_rad = -3.85
tilt_angle_rad_1 = -0.8
tilt_angle_rad_2 = -1.4
lift_m = 0.74
pose = {'joint_lift': lift_m}
rospy.loginfo('Move to second shoulder capture height.')
self.move_to_pose(pose)
rospy.loginfo('Wait {0} seconds for the robot to settle after motion of the arm.'.format(wrist_motion_settle_time))
rospy.sleep(wrist_motion_settle_time)
first_pan_tilt = True
observation = self.get_samples(pan_angle_rad, tilt_angle_rad_1, wrist_extension, number_of_samples_per_head_pose, first_move=first_pan_tilt)
calibration_data.extend(observation)
if not collect_check_data:
first_pan_tilt = False
observation = self.get_samples(pan_angle_rad, tilt_angle_rad_2, wrist_extension, number_of_samples_per_head_pose, first_move=first_pan_tilt)
calibration_data.extend(observation)
#######################################
##
## COLLECT ARM FOCUSED DATA
##
#######################################
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('COLLECT ARM FOCUSED DATA')
rospy.loginfo('*************************************')
rospy.loginfo('')
##############################################################
### poses that see both of the arm markers
#
# lift height 0.8
# extension 0.4 tilt -0.52 pan -2.06 to -1.45
# extension 0.03 tilt -1.27 pan -2.33 to -1.48
#
# lift height 0.2
# extension 0.4 tilt -0.9 pan -2.9 to -0.93
# extension 0.03 tilt -1.23 pan -3.2 to -0.4 (full range)
#
# ranges with good coverage
# (needs to account for +/- 0.07 rad for pan backlash)
# tilt -1.2 to -0.5
# pan -2.0 to -1.45
#
##############################################################
wrist_focused_min_pan = -2.0
wrist_focused_max_pan = -1.45
wrist_focused_min_tilt = -1.2
wrist_focused_max_tilt = -0.5
if collect_check_data:
num_wrist_focused_pan_steps = 1 #3
num_wrist_focused_tilt_steps = 1 #3
else:
num_wrist_focused_pan_steps = 2 #3
num_wrist_focused_tilt_steps = 2 #3
wrist_focused_pan_angles_rad = np.linspace(wrist_focused_min_pan, wrist_focused_max_pan, num_wrist_focused_pan_steps)
wrist_focused_tilt_angles_rad = np.linspace(wrist_focused_min_tilt, wrist_focused_max_tilt, num_wrist_focused_tilt_steps)
if collect_check_data:
wrist_extensions = [0.2]
# Already have data from a single extension at a height of
# 1.2m from global camera looking down, so try using only 2
# heights.
#
# 0.46 m = (0.8 m + 0.12 m) / 2.0
wrist_heights = [0.46, 0.8] #[0.2, 0.5, 0.8]
wrist_poses = [{'wrist_extension': e, 'joint_lift': h} for h in wrist_heights for e in wrist_extensions]
num_wrist_poses = len(wrist_poses)
else:
wrist_extensions = [0.03, 0.4]
# Already have data from a single extension at a height of
# 1.2m from global camera looking down, so try using only 2
# heights.
#
# 0.46 m = (0.8 m + 0.12 m) / 2.0
wrist_heights = [0.46, 0.8] #[0.2, 0.5, 0.8]
wrist_poses = [{'wrist_extension': e, 'joint_lift': h} for h in wrist_heights for e in wrist_extensions]
num_wrist_poses = len(wrist_poses)
rospy.loginfo('Starting to collect arm focused samples (expect to collect {0} samples).'.format(num_wrist_poses * num_wrist_focused_pan_steps * num_wrist_focused_tilt_steps))
for wrist_pose in wrist_poses:
rospy.loginfo('Move to a new arm pose.')
self.move_to_pose(wrist_pose)
# wait for the joints and sensors to settle
rospy.loginfo('Wait {0} seconds for the robot to settle after motion of the arm.'.format(wrist_motion_settle_time))
rospy.sleep(wrist_motion_settle_time)
first_pan_tilt = True
for pan_angle in wrist_focused_pan_angles_rad:
for tilt_angle in wrist_focused_tilt_angles_rad:
observation = self.get_samples(pan_angle, tilt_angle, wrist_pose['wrist_extension'], number_of_samples_per_head_pose, first_move=first_pan_tilt)
first_pan_tilt = False
calibration_data.extend(observation)
n = len(calibration_data)
if (n % 10) == 0:
rospy.loginfo('{0} samples collected so far.'.format(n))
#######################################
##
## FINISHED COLLECTING DATA - SAVE IT
##
#######################################
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('FINISHED COLLECTING DATA')
rospy.loginfo('*************************************')
rospy.loginfo('')
rospy.loginfo('Collected {0} samples in total.'.format(len(calibration_data)))
t = time.localtime()
capture_date = str(t.tm_year) + str(t.tm_mon).zfill(2) + str(t.tm_mday).zfill(2) + str(t.tm_hour).zfill(2) + str(t.tm_min).zfill(2)
if collect_check_data:
filename = self.calibration_directory + 'check_head_calibration_data_' + capture_date + '.yaml'
else:
filename = self.calibration_directory + 'head_calibration_data_' + capture_date + '.yaml'
rospy.loginfo('Saving calibration_data to a YAML file named {0}'.format(filename))
fid = open(filename, 'w')
yaml.dump(calibration_data, fid)
fid.close()
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('')
#######################################
def testrig_data_collect(self, number_of_samples):
calibration_data = []
########################################
##
## COLLECT DATA FROM TEST RIG
##
########################################
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('COLLECT DATA FROM TEST RIG')
rospy.loginfo('*************************************')
rospy.loginfo('')
while len(calibration_data)<=number_of_samples:
observation = self.get_samples_testrig(1)
first_pan_tilt = False
calibration_data.extend(observation)
n = len(calibration_data)
if (n % 10) == 0:
rospy.loginfo('{0} samples collected so far.'.format(n))
#######################################
##
## FINISHED COLLECTING DATA - SAVE IT
##
#######################################
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('FINISHED COLLECTING DATA')
rospy.loginfo('*************************************')
rospy.loginfo('')
rospy.loginfo('Collected {0} samples in total.'.format(len(calibration_data)))
t = time.localtime()
capture_date = str(t.tm_year) + str(t.tm_mon).zfill(2) + str(t.tm_mday).zfill(2) + str(t.tm_hour).zfill(2) + str(t.tm_min).zfill(2)
temp_loc = '/home/hello-robot/catkin_ws/src/stretch_ros/stretch_camera_testrig/data/' #Change it
filename = temp_loc + 'testrig_collected_data_' + capture_date + '.yaml'
rospy.loginfo('Saving calibration_data to a YAML file named {0}'.format(filename))
fid = open(filename, 'w')
yaml.dump(calibration_data, fid)
fid.close()
rospy.loginfo('')
rospy.loginfo('*************************************')
rospy.loginfo('')
#######################################
def move_to_initial_configuration(self):
# The robot is commanded to move to this pose prior to
# beginning to collect calibration data.
initial_pose_map = {
'joint_wrist_yaw': 0.0,
'joint_wrist_pitch': 0.0,
'joint_wrist_roll': 0.0,
'wrist_extension': 0.0,
'joint_lift': 0.3,
'gripper_aperture': 0.0,
'joint_head_pan': -1.6947147036864942,
'joint_head_tilt': -0.4
}
initial_pose = {}
for j in self.joint_state.name:
if j in initial_pose_map:
initial_pose[j] = initial_pose_map[j]
rospy.loginfo('Move to the calibration start pose.')
self.move_to_pose(initial_pose)
def main(self, collect_check_data):
rospy.init_node('collect_head_calibration_data')
self.node_name = rospy.get_name()
rospy.loginfo("{0} started".format(self.node_name))
# Obtain the ArUco marker ID numbers.
self.marker_info = rospy.get_param('/aruco_marker_info')
for k in self.marker_info.keys():
m = self.marker_info[k]
if m['link'] == 'link_aruco_left_base':
self.base_left_marker_id = int(k)
if m['link'] == 'link_aruco_right_base':
self.base_right_marker_id = int(k)
if m['link'] == 'link_aruco_inner_wrist':
self.wrist_inside_marker_id = int(k)
if m['link'] == 'link_aruco_top_wrist':
self.wrist_top_marker_id = int(k)
if m['link'] == 'link_aruco_shoulder':
self.shoulder_marker_id = int(k)
if not self.test_rig:
filename = rospy.get_param('~controller_calibration_file')
rospy.loginfo('Loading factory default tilt backlash transition angle from the YAML file named {0}'.format(filename))
fid = open(filename, 'r')
controller_parameters = yaml.load(fid)
fid.close()
self.tilt_angle_backlash_transition_rad = controller_parameters['tilt_angle_backlash_transition']
deg_per_rad = 180.0/math.pi
rospy.loginfo('self.tilt_angle_backlash_transition_rad in degrees = {0}'.format(self.tilt_angle_backlash_transition_rad * deg_per_rad))
self.calibration_directory = rospy.get_param('~calibration_directory')
rospy.loginfo('Using the following directory for calibration files: {0}'.format(self.calibration_directory))
# Setup time synchronization for calibration data.
joint_state_subscriber = message_filters.Subscriber('/stretch/joint_states', JointState)
accel_subscriber = message_filters.Subscriber('/camera/accel/sample_corrected', Imu)
aruco_subscriber = message_filters.Subscriber('/aruco/marker_array', MarkerArray)
slop_time = 0.1
self.synchronizer = message_filters.ApproximateTimeSynchronizer([accel_subscriber, aruco_subscriber, joint_state_subscriber], 10, slop_time, allow_headerless=True)
self.synchronizer.registerCallback(self.calibration_data_callback)
self.trajectory_client = actionlib.SimpleActionClient('/stretch_controller/follow_joint_trajectory', FollowJointTrajectoryAction)
self.trajectory_goal = FollowJointTrajectoryGoal()
self.trajectory_goal.goal_time_tolerance = rospy.Time(1.0)
self.point = JointTrajectoryPoint()
self.point.time_from_start = rospy.Duration(0.0)
server_reached = self.trajectory_client.wait_for_server(timeout=rospy.Duration(60.0))
if not server_reached:
rospy.signal_shutdown('Unable to connect to arm action server. Timeout exceeded.')
sys.exit()
rate = rospy.Rate(self.rate)
while self.joint_state is None:
rate.sleep()
self.move_to_initial_configuration()
self.calibrate_pan_and_tilt(collect_check_data)
else:
# Setup time synchronization for collecting testrig data.
accel_subscriber = message_filters.Subscriber('/camera/accel/sample_corrected', Imu)
aruco_subscriber = message_filters.Subscriber('/aruco/marker_array', MarkerArray)
slop_time = 0.1
self.synchronizer = message_filters.ApproximateTimeSynchronizer([accel_subscriber, aruco_subscriber], 10, slop_time, allow_headerless=True)
self.synchronizer.registerCallback(self.calibration_data_callback)
rate = rospy.Rate(self.rate)
self.testrig_data_collect(number_of_samples=self.number_of_testrig_samples)
if __name__ == '__main__':
parser = ap.ArgumentParser(description='Collect head calibration data.')
parser.add_argument('--check', action='store_true', help='Collect data to check the current calibration, instead of data to perform a new calibration.')
args, unknown = parser.parse_known_args()
collect_check_data = args.check
try:
node = CollectHeadCalibrationDataNode()
node.main(collect_check_data)
except rospy.ROSInterruptException:
pass

+ 38
- 0
stretch_camera_testrig/nodes/test_basic.py View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
# Goal Aruco Marker Detection subscribe
import rospy
import actionlib
import math
import time
import threading
import sys
import argparse as ap
import numpy as np
import ros_numpy
import yaml
import time
from sensor_msgs.msg import JointState
from geometry_msgs.msg import Twist
from nav_msgs.msg import Odometry
from std_msgs.msg import String
from visualization_msgs.msg import Marker
from visualization_msgs.msg import MarkerArray
from collect_head_calibration_data import CollectHeadCalibrationDataNode
if __name__ == '__main__':
parser = ap.ArgumentParser(description='Collect Test Rig data.')
parser.add_argument('--check', action='store_true', help='Collect data to check the current calibration, instead of data to perform a new calibration.')
args, unknown = parser.parse_known_args()
collect_check_data = args.check
try:
node = CollectHeadCalibrationDataNode(test_rig=True)
node.number_of_testrig_samples = 200
node.main(collect_check_data)
except rospy.ROSInterruptException:
pass

+ 333
- 0
stretch_camera_testrig/rviz/rviz.rviz View File

@ -0,0 +1,333 @@
Panels:
- Class: rviz/Displays
Help Height: 78
Name: Displays
Property Tree Widget:
Expanded:
- /Global Options1
- /Status1
- /TF1/Frames1
Splitter Ratio: 0.5
Tree Height: 728
- Class: rviz/Selection
Name: Selection
- Class: rviz/Tool Properties
Expanded:
- /2D Pose Estimate1
- /2D Nav Goal1
- /Publish Point1
Name: Tool Properties
Splitter Ratio: 0.5886790156364441
- Class: rviz/Views
Expanded:
- /Current View1
Name: Views
Splitter Ratio: 0.5
- Class: rviz/Time
Experimental: false
Name: Time
SyncMode: 0
SyncSource: PointCloud2
Preferences:
PromptSaveOnExit: true
Toolbars:
toolButtonStyle: 2
Visualization Manager:
Class: ""
Displays:
- Alpha: 0.5
Cell Size: 1
Class: rviz/Grid
Color: 160; 160; 164
Enabled: true
Line Style:
Line Width: 0.029999999329447746
Value: Lines
Name: Grid
Normal Cell Count: 0
Offset:
X: 0
Y: 0
Z: 0
Plane: XY
Plane Cell Count: 10
Reference Frame: <Fixed Frame>
Value: true
- Alpha: 1
Class: rviz/RobotModel
Collision Enabled: false
Enabled: true
Links:
All Links Enabled: true
Expand Joint Details: false
Expand Link Details: false
Expand Tree: false
Link Tree Style: Links in Alphabetic Order
base_link:
Alpha: 1
Show Axes: false
Show Trail: false
camera_accel_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_accel_optical_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_bottom_screw_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_color_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_color_optical_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_depth_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_depth_optical_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_gyro_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_gyro_optical_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_infra1_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_infra1_optical_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_infra2_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_infra2_optical_frame:
Alpha: 1
Show Axes: false
Show Trail: false
camera_link:
Alpha: 1
Show Axes: false
Show Trail: false
Value: true
Name: RobotModel
Robot Description: robot_description
TF Prefix: ""
Update Interval: 0
Value: true
Visual Enabled: true
- Class: rviz/MarkerArray
Enabled: true
Marker Topic: /aruco/marker_array
Name: MarkerArray
Namespaces:
"": true
Queue Size: 100
Value: true
- Class: rviz/MarkerArray
Enabled: true
Marker Topic: /aruco/axes
Name: MarkerArray
Namespaces:
"": true
Queue Size: 100
Value: true
- Alpha: 1
Autocompute Intensity Bounds: true
Autocompute Value Bounds:
Max Value: 10
Min Value: -10
Value: true
Axis: Z
Channel Name: intensity
Class: rviz/PointCloud2
Color: 255; 255; 255
Color Transformer: RGB8
Decay Time: 0
Enabled: true
Invert Rainbow: false
Max Color: 255; 255; 255
Min Color: 0; 0; 0
Name: PointCloud2
Position Transformer: XYZ
Queue Size: 10
Selectable: true
Size (Pixels): 3
Size (m): 0.009999999776482582
Style: Points
Topic: /camera/depth/color/points
Unreliable: false
Use Fixed Frame: true
Use rainbow: true
Value: true
- Class: rviz/TF
Enabled: true
Frame Timeout: 15
Frames:
All Enabled: false
base_left:
Value: true
base_link:
Value: true
base_right:
Value: true
camera_accel_frame:
Value: false
camera_accel_optical_frame:
Value: false
camera_aligned_depth_to_color_frame:
Value: false
camera_bottom_screw_frame:
Value: false
camera_color_frame:
Value: false
camera_color_optical_frame:
Value: false
camera_depth_frame:
Value: false
camera_depth_optical_frame:
Value: false
camera_gyro_frame:
Value: false
camera_gyro_optical_frame:
Value: false
camera_infra1_frame:
Value: false
camera_infra1_optical_frame:
Value: false
camera_infra2_frame:
Value: false
camera_infra2_optical_frame:
Value: false
camera_link:
Value: false
shoulder_top:
Value: true
unknown:
Value: false
wrist_inside:
Value: true
wrist_top:
Value: true
Marker Scale: 0.20000000298023224
Name: TF
Show Arrows: true
Show Axes: true
Show Names: true
Tree:
base_link:
camera_bottom_screw_frame:
camera_link:
camera_accel_frame:
camera_accel_optical_frame:
{}
camera_aligned_depth_to_color_frame:
{}
camera_color_frame:
camera_color_optical_frame:
base_left:
{}
base_right:
{}
shoulder_top:
{}
unknown:
{}
wrist_inside:
{}
wrist_top:
{}
camera_depth_frame:
camera_depth_optical_frame:
{}
camera_gyro_frame:
camera_gyro_optical_frame:
{}
camera_infra1_frame:
camera_infra1_optical_frame:
{}
camera_infra2_frame:
camera_infra2_optical_frame:
{}
Update Interval: 0
Value: true
Enabled: true
Global Options:
Background Color: 48; 48; 48
Default Light: true
Fixed Frame: base_link
Frame Rate: 30
Name: root
Tools:
- Class: rviz/Interact
Hide Inactive Objects: true
- Class: rviz/MoveCamera
- Class: rviz/Select
- Class: rviz/FocusCamera
- Class: rviz/Measure
- Class: rviz/SetInitialPose
Theta std deviation: 0.2617993950843811
Topic: /initialpose
X std deviation: 0.5
Y std deviation: 0.5
- Class: rviz/SetGoal
Topic: /move_base_simple/goal
- Class: rviz/PublishPoint
Single click: true
Topic: /clicked_point
Value: true
Views:
Current:
Class: rviz/Orbit
Distance: 0.4894060790538788
Enable Stereo Rendering:
Stereo Eye Separation: 0.05999999865889549
Stereo Focal Distance: 1
Swap Stereo Eyes: false
Value: false
Focal Point:
X: 0.27744147181510925
Y: -0.002162957563996315
Z: 0.0087382011115551
Focal Shape Fixed Size: true
Focal Shape Size: 0.05000000074505806
Invert Z Axis: false
Name: Current View
Near Clip Distance: 0.009999999776482582
Pitch: 0.4003978967666626
Target Frame: <Fixed Frame>
Value: Orbit (rviz)
Yaw: 2.0953991413116455
Saved: ~
Window Geometry:
Displays:
collapsed: true
Height: 1025
Hide Left Dock: true
Hide Right Dock: true
QMainWindow State: 000000ff00000000fd00000004000000000000015600000363fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073000000003d00000363000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f00000363fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d00000363000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000073d0000003efc0100000002fb0000000800540069006d006501000000000000073d000002eb00fffffffb0000000800540069006d006501000000000000045000000000000000000000073d0000036300000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
Selection:
collapsed: false
Time:
collapsed: false
Tool Properties:
collapsed: false
Views:
collapsed: true
Width: 1853
X: 67
Y: 27

Loading…
Cancel
Save