dense_laser_cache.py
Go to the documentation of this file.
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2008, Willow Garage, Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of Willow Garage, Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 
34 import roslib
35 roslib.load_manifest('dense_laser_assembler')
36 
37 import time
38 import rospy
39 import sys
40 import threading
41 from laser_scan.msg import *
42 from roslib import rostime
43 
44 # Stores a single laser scan plus some meta information
45 class LaserCacheElem() :
46  pass
47 
48 
49 class DenseLaserCache() :
50  def __init__(self, req_callback, max_mech_len, max_laser_done_len, max_laser_wait_len) :
51 
52  # Define callback for interval cloud req
53  self._req_callback = req_callback
54 
55  # Specify all queue lengths
56  self._max_mech_len = max_mech_len
57  self._max_laser_done_len = max_laser_done_len
58  self._max_laser_wait_len = max_laser_wait_len
59 
60  # Init mutexes
61  self._mech_lock = threading.Lock()
62  self._laser_done_lock = threading.Lock()
63  self._laser_wait_lock = threading.Lock()
64  self._interval_req_lock = threading.Lock()
65 
66  # Define queues
67  self._mech_q = [ ] # Mechanism State Data
68  self._laser_done_q = [] # Laser scans that have already been processed
69  self._laser_wait_q = [] # Laser scans that we've received but haven't/can't processed yet
70  self._interval_req_q = [] # Stores list of intervals that we need to process
71 
72  def add_mech_state(self, msg) :
73  self._mech_lock.acquire()
74  self._mech_q.append(msg)
75  while len(self._mech_q) > self._max_mech_len :
76  self._mech_q.pop(0)
77  self._mech_lock.release()
78 
79  def add_scan(self, msg) :
80  self._laser_wait_lock.acquire()
81  self._laser_wait_q.append(msg)
82  while len(self._laser_wait_q) > self._max_laser_wait_len :
83  self._laser_wait_q.pop(0)
84  self._laser_wait_lock.release()
85 
86  # Iterate through the list of scans we're waiting to process, and process each one
87  # if we're ready. A scan is ready to be processed if there is at least one
88  # MechanismState message available before and after the scan. The processed scan is
89  # then added to the laser_done queue
90  def process_scans(self) :
91  self._mech_lock.acquire()
92 
93  if len(self._mech_q) > 0 :
94  oldest_mech_time = self._mech_q[0].header.stamp
95  newest_mech_time = self._mech_q[-1].header.stamp
96  else :
97  self._mech_lock.release()
98  return
99 
100  self._laser_wait_lock.acquire()
101 
102  scan_interval = [ (x.header.stamp,
103 # x.header.stamp + rostime.Duration().from_seconds(0.0) )
104  x.header.stamp+rostime.Duration().from_seconds(len(x.ranges)*x.time_increment))
105  for x in self._laser_wait_q ]
106 
107  laser_pending_q = [x for i,x in enumerate(self._laser_wait_q)
108  if scan_interval[i][1] < newest_mech_time and
109  scan_interval[i][0] > oldest_mech_time]
110  # Purge entire queue, except scans in the future
111  self._laser_wait_q = [x for i,x in enumerate(self._laser_wait_q)
112  if scan_interval[i][1] >= newest_mech_time]
113 
114  self._laser_wait_lock.release()
115 
116  # Now process the pending queue
117  processed = [self._process_pending(x,self._mech_q) for x in laser_pending_q]
118 
119  #if len(processed) > 0 :
120  # print 'processed %i scans' % len(processed)
121 
122  self._laser_done_lock.acquire()
123  self._laser_done_q = self._laser_done_q + processed
124  while len(self._laser_done_q) > self._max_laser_done_len :
125  self._laser_done_q.pop(0)
126  self._laser_done_lock.release()
127  self._mech_lock.release()
128  # Process a scan, assuming that we have MechansimState data available before and after the scan
129  # \param scan_msg The message that we want to process
130  # \param the time-order queue of mechanism_state data
131  # \param The processed scan element
132  def _process_pending(self, scan_msg, mech_q):
133  scan_end = scan_msg.header.stamp + rostime.Duration().from_seconds(len(scan_msg.ranges)*scan_msg.time_increment)
134  mech_before = [x for x in mech_q if x.header.stamp < scan_msg.header.stamp][-1]
135  mech_after = [x for x in mech_q if x.header.stamp > scan_end][0]
136 
137  # Get the joint position and time associated with the MechanismState before the scan
138  ind = [x.name for x in mech_before.joint_states].index('laser_tilt_mount_joint')
139  pos_before = mech_before.joint_states[ind].position
140  time_before= mech_before.header.stamp
141 
142  # Get the joint position and time associated with the MechanismState before the scan
143  ind = [x.name for x in mech_after.joint_states].index('laser_tilt_mount_joint')
144  pos_after = mech_after.joint_states[ind].position
145  time_after= mech_after.header.stamp
146 
147  elapsed = (time_after - time_before).to_seconds()
148 
149  # Linear interp
150  #pos_during = elapsed_after/elapsed_total*pos_before + elapsed_before/elapsed_total*pos_after
151 
152  #key_rays = range(0, len(scan_msg.ranges))
153  key_rays = [0, len(scan_msg.ranges)-1]
154  time_during = [ scan_msg.header.stamp
155  + rostime.Duration().from_seconds(scan_msg.time_increment*x)
156  for x in key_rays]
157  time_normalized = [ (x - time_before).to_seconds()/elapsed for x in time_during]
158  if any( [x > 1 for x in time_normalized]) or any( [x < 0 for x in time_normalized] ) :
159  print('Error computing normalized time')
160  pos_during = [ pos_before * (1-x) + pos_after * x for x in time_normalized ]
161 
162  elem = LaserCacheElem()
163  elem.pos = pos_during
164  elem.scan = scan_msg
165  return elem
166 
167  # Returns a list of processed scan elems between the start and end time
168  def get_dense_cloud(self, start, end) :
169  self._laser_done_lock.acquire()
170  trimmed_done = [x for x in self._laser_done_q
171  if x.scan.header.stamp <= end and
172  x.scan.header.stamp >= start]
173  self._laser_done_lock.release()
174  return trimmed_done
175 
176  # Returns the time associated with the latest scan
178  self._laser_done_lock.acquire()
179  if len(self._laser_done_q) > 0 :
180  latest_time = self._laser_done_q[-1].scan.header.stamp
181  else :
182  latest_time = rostime.Time().from_seconds(0.0)
183  self._laser_done_lock.release()
184  return latest_time
185 
186  # Adds a request for extracting data during an interval. This request will be attempted to
187  # processed when process_interval_reqs is called.
188  def add_interval_req(self, start, end) :
189  self._interval_req_lock.acquire()
190  self._interval_req_q.append([start, end])
191  self._interval_req_lock.release()
192  self.process_interval_reqs()
193 
194  # Attempts to service all the interval requests added by calling add_interval_req. A req can
195  # be serviced if there exists a processed scan elem with a timestamp greater than the end time
196  # of the interval request
198 
199  latest_time = self.get_latest_done_scan_time()
200 
201  # Split list into request we're ready to process, on one's we're not ready for
202  self._interval_req_lock.acquire()
203  pending_reqs = [x for x in self._interval_req_q if x[1] < latest_time]
204  idle_reqs = [x for x in self._interval_req_q if x[1] >= latest_time]
205  self._interval_req_q = idle_reqs
206  self._interval_req_lock.release()
207 
208  # Compute clouds for the requests we're ready to process
209  clouds = [self.get_dense_cloud(x[0], x[1]) for x in pending_reqs]
210 
211  for x in clouds :
212  self._req_callback(x)
def __init__(self, req_callback, max_mech_len, max_laser_done_len, max_laser_wait_len)


dense_laser_assembler
Author(s): Vijay Pradeep
autogenerated on Tue Jun 1 2021 02:50:54