import zmq from breezyslam.algorithms import RMHC_SLAM from breezyslam.sensors import RPLidarA1 as LaserModel from slam.SlamController_pb2 import SlamScan, SlamLocation import messaging.message_factory as mf import messaging.messages as messages import tracking.devices.factory as lidar_fact # Left here as was used in the example, configure as necessary. # MAP_SIZE_PIXELS = 500 # MAP_SIZE_METERS = 10 # LIDAR_DEVICE = '/dev/ttyUSB0' class SlamStreamer: can_scan = False def __init__(self, map_pixels=None, map_meters=None, port=None): self._map_pixels = map_pixels self._map_meters = map_meters self._port = port def start(self): ''' Does scanning and constructs the slam map, and pushes to subscribers through a zmq pub socket. This is done on the main thread, so you'll need to run this method on a separate thread yourself. All constructor parameters must be set prior to calling this method, and changing those values after calling this method will have no effect. ''' self.can_scan = True print('Starting to stream') self._mFactory = mf.getZmqPubSubStreamer(self._port) print('Started and bound zmq socket.') # Adapted from BreezySLAM rpslam example. # Connect to Lidar unit. For some reason it likes to be done twice, otherwise it errors out... lidar = lidar_fact.get_lidar() lidar = lidar_fact.get_lidar() print('Initialised lidar') # Create an RMHC SLAM object with a laser model and optional robot model slam = RMHC_SLAM(LaserModel(), self._map_pixels, self._map_meters) print('initialised slam') # Initialize empty map mapbytes = bytearray(self.map_pixels * self.map_pixels) print('Initialised byte []') # Create an iterator to collect scan data from the RPLidar iterator = lidar.iter_scans() print('Scanning') while self.can_scan: # Extract (quality, angle, distance) triples from current scan items = [item for item in next(iterator)] # Extract distances and angles from triples distances = [item[2] for item in items] angles = [item[1] for item in items] print('Updating map') # Update SLAM with current Lidar scan and scan angles slam.update(distances, scan_angles_degrees=angles) print('Map updated') slam.getmap(mapbytes) self._push_map(mapbytes, slam.getpos()) def _push_map(self, mapbytes, location): ''' Pushes a scan over zmq using protocol buffers. map should be the result of slam.getmap. location should be a tuple, the result of slam.getpos() ''' protoScan = messages.ProtoMessage(message=SlamScan(map=bytes(mapbytes), location=SlamLocation(x=location[0], y=location[1], theta=location[2]))) print('Sending map') self._mFactory.send_message_topic( 'slam_map', protoScan) def stop_scanning(self): self.can_scan = False # Properties @property def map_pixels(self): return self._map_pixels @map_pixels.setter def map_pixels(self, value): self._map_pixels = value @property def map_meters(self): return self._map_meters @map_meters.setter def map_meters(self, value): self._map_meters = value @property def lidar_connection(self): return self._lidar_connection @lidar_connection.setter def lidar_connection(self, value): self._lidar_connection = value @property def port(self): return self._port @port.setter def port(self, value): self._port = value