simulated_tactile_sensors.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 
18 import rospy
19 from schunk_sdh.msg import *
20 from gazebo_msgs.msg import *
21 
22 
24 
25  def __init__(self, topic_name, finger_length, finger_width):
26  self.finger_length = finger_length
27  self.finger_width = finger_width
28 
29  # get parameters from parameter server
30  self.cells_x = rospy.get_param("~cells_x", 6)
31  self.cells_y = rospy.get_param("~cells_y", 14)
32  rospy.logdebug("size of the tactile matrix is %ix%i patches", self.cells_x, self.cells_y)
33 
34  self.sensitivity = rospy.get_param("~sensitivity", 350.0)
35  rospy.logdebug("sensitivity: %f", self.sensitivity)
36 
37  self.range = rospy.get_param("~output_range", 3500)
38  rospy.logdebug("output range: %f", self.range)
39 
40  self.filter_length = rospy.get_param("~filter_length", 10)
41  rospy.logdebug("filter length: %i", self.filter_length)
42 
43  rospy.Subscriber(topic_name, ContactsState, self.contact_callback)
44  rospy.logdebug("subscribed to bumper states topic: %s", topic_name)
45 
47 
48 
49  '''
50  Update the pad with the Gazebo contact information.
51 
52  :param states: gazebo_msgs.msg.ContactsState
53  '''
54  def contact_callback(self, states):
55  matrix = self.create_empty_force_matrix()
56  matrix = self.update(states, matrix)
57  matrix = self.smooth(matrix)
58  matrix = self.time_average(self.smoothed_matrix, matrix, self.filter_length)
59 
60  self.smoothed_matrix = matrix
61 
62 
63  '''
64  Update the provided matrix with the contact information from the Gazebo
65  simulator.
66 
67  :param states: gazebo_msgs.msg.ContactsState
68  :param matrix: Float[]
69  '''
70  def update(self, states, matrix):
71  # no contact, so we don't update the matrix
72  if (len(states.states) == 0):
73  return matrix
74 
75  for i in range(len(states.states)):
76  state = states.states[i]
77  for j in range(len(state.contact_positions)):
78  # check if the contact is on the same side as the sensor
79  if (state.contact_positions[j].x < 0.0):
80  continue
81 
82  # normalize the contact position along the tactile sensor
83  # we assume that the tactile sensor occupies the complete
84  # surface of the inner finger side, so finger size is equal to
85  # sensor size
86  normalized_x = (state.contact_positions[j].y / self.finger_width) + 0.5
87  normalized_y = state.contact_positions[j].z / self.finger_length
88 
89  # from the normalized coordinate we can now determine the index
90  # of the tactile patch that is activated by the contact
91  x = round(normalized_x * self.cells_x)
92  y = round(normalized_y * self.cells_y)
93 
94  force = -state.wrenches[j].force.x
95  if (force < 0.0): force = 0.0
96 
97  index = int(self.get_index(x, y))
98 
99  matrix[index] += force
100 
101  return matrix
102 
103 
104  '''
105  Create a new matrix that contains the current force on each cell. Initialize
106  all cells with zeros.
107 
108  :return: Float[]
109  '''
111  matrix = []
112  for i in range(self.cells_x * self.cells_y):
113  matrix.append(0.0)
114 
115  return matrix
116 
117 
118  '''
119  Run a moving average on the provided matrix.
120 
121  :param matrix: Float[]
122  :return: Float[]
123  '''
124  def smooth(self, matrix):
125  smoothed_matrix = self.create_empty_force_matrix()
126 
127  for x in range(0, self.cells_x):
128  for y in range(0, self.cells_y):
129  sum = 0.0
130  count = 0
131  for dx in range(-1, 2):
132  for dy in range(-1, 2):
133  index_x = x + dx
134  index_y = y + dy
135 
136  if (index_x < 0 or index_x >= self.cells_x): continue
137  if (index_y < 0 or index_y >= self.cells_y): continue
138 
139  index = self.get_index(index_x, index_y)
140  sum += matrix[index]
141  count += 1
142  index = self.get_index(x, y)
143  smoothed_matrix[index] = sum / count
144 
145  return smoothed_matrix
146 
147 
148  '''
149  Calculate the average matrix from the force buffer.
150 
151  :return: Float[]
152  '''
153  def time_average(self, matrix_buffer, current_matrix, filter_length):
154  matrix = self.create_empty_force_matrix()
155  sample_factor = 1.0 / filter_length
156 
157  for i in range(self.cells_x * self.cells_y):
158  force = (1.0 - sample_factor) * matrix_buffer[i]
159  force += sample_factor * current_matrix[i]
160  matrix[i] = force
161 
162  return matrix
163 
164 
165  '''
166  Get the current forces as tactile matrix. matrix_id is the identifier of the
167  tactile matrix and determines which pad produced the data.
168 
169  :param matrix_id: Integer
170  :return: schunk_sdh.msg.TactileMatrix
171  '''
172  def tactile_matrix(self, matrix_id):
173  matrix = TactileMatrix()
174  matrix.matrix_id = matrix_id
175  matrix.cells_x = self.cells_x
176  matrix.cells_y = self.cells_y
177 
178  m = self.smoothed_matrix
179 
180  for i in range(self.cells_x * self.cells_y):
181  force = m[i] * self.sensitivity
182  if (force < 0.0): force = 0.0
183  if (force > self.range): force = self.range
184  matrix.tactile_array.append(force)
185 
186  return matrix
187 
188 
189  '''
190  Map the two-dimensional coordinate of a tactile patch to an index in the
191  one-dimensional data array. The coordinates are bound to the upper and lower
192  limits.
193 
194  :param x: Integer
195  :param y: Integer
196  :return: Integer
197  '''
198  def get_index(self, x, y):
199  y = self.cells_y - y - 1
200  if (x >= self.cells_x): x = self.cells_x - 1
201  if (x < 0): x = 0
202  if (y >= self.cells_y): y = self.cells_y - 1
203  if (y < 0): y = 0
204 
205  return y * self.cells_x + x
206 
207 
208 
209 
211  '''
212  Constants that determine which indices the finger parts have in the
213  schunk_sdh.msg.TactileSensor matrix.
214  '''
215  ID_FINGER_12 = 0
216  ID_FINGER_13 = 1
217  ID_THUMB_2 = 2
218  ID_THUMB_3 = 3
219  ID_FINGER_22 = 4
220  ID_FINGER_23 = 5
221 
222 
223  def __init__(self):
224  self.pads = []
225  self.pads.append(GazeboTactilePad("thumb_2/state", 0.0865, 0.03))
226  self.pads.append(GazeboTactilePad("thumb_3/state", 0.0675, 0.03))
227  self.pads.append(GazeboTactilePad("finger_12/state", 0.0865, 0.03))
228  self.pads.append(GazeboTactilePad("finger_13/state", 0.0675, 0.03))
229  self.pads.append(GazeboTactilePad("finger_22/state", 0.0865, 0.03))
230  self.pads.append(GazeboTactilePad("finger_23/state", 0.0675, 0.03))
231 
232  self.pub = rospy.Publisher("tactile_data", TactileSensor, queue_size=1)
233  rospy.loginfo("'tactile_data' topic advertized")
234 
235 
236  '''
237  Publish the current state of the simulated tactile sensors.
238  '''
239  def publish(self):
240  msg = TactileSensor()
241  msg.header.stamp = rospy.Time.now()
242  for i in range(6):
243  msg.tactile_matrix.append(self.pads[i].tactile_matrix(i))
244  self.pub.publish(msg)
245 
246 
247 
248 
249 if __name__ == "__main__":
250  rospy.init_node('tactile_sensors')
251  rospy.sleep(0.5)
252 
254 
255  while not rospy.is_shutdown():
256  sensor.publish()
257  rospy.sleep(0.05)
def __init__(self, topic_name, finger_length, finger_width)
def time_average(self, matrix_buffer, current_matrix, filter_length)


schunk_simulated_tactile_sensors
Author(s): Sven Schneider
autogenerated on Mon Nov 25 2019 03:48:25