def simulate(batch_file, preset_filename, bot_paths, seed, override_settings, *queues_sizes): result_queue = queues_sizes[0][0] result_queue._internal_size = queues_sizes[0][1] StateHandler.instance.shared_info = { "result_queue": result_queue, } send_queues = [q for q, _ in queues_sizes[1::2]] for i, (_, size) in enumerate(queues_sizes[1::2]): send_queues[i]._internal_size = size recv_queues = [q for q, _ in queues_sizes[2::2]] for i, (_, size) in enumerate(queues_sizes[2::2]): recv_queues[i]._internal_size = size Randomiser.createGlobalRandomiserWithSeed(seed) preset_file = find_abs(preset_filename, allowed_areas=preset_locations()) with open(preset_file, "r") as f: config = yaml.safe_load(f) config["settings"] = config.get("settings", {}) recursive_merge(config["settings"], override_settings) config["robots"] = config.get("robots", []) + bot_paths initialiseFromConfig(config, send_queues, recv_queues)
def resetPositions(self): for team in range(len(self.spawns)): for index in range(self.BOTS_PER_TEAM): actual_index = team + index * len(self.spawns) if actual_index >= len(self.robots): break # Generate a uniformly random point in radius of spawn for bot. diff_radius = Randomiser.random() * self.BOT_SPAWN_RADIUS diff_angle = Randomiser.random() * 2 * np.pi self.robots[actual_index].body.position = [ self.spawns[team][index][0][0] + diff_radius * np.cos(diff_angle), self.spawns[team][index][0][1] + diff_radius * np.sin(diff_angle), ] self.robots[actual_index].body.angle = self.spawns[team][ index][1] * np.pi / 180 self.robots[actual_index].body.velocity = (0.0, 0.0) self.robots[actual_index].body.angular_velocity = 0 # Generate position for ball. diff_radius = Randomiser.random() * self.BALL_SPAWN_RADIUS diff_angle = Randomiser.random() * 2 * np.pi ScriptLoader.instance.object_map["IR_BALL"].body.position = [ diff_radius * np.cos(diff_angle), diff_radius * np.sin(diff_angle) - 18, ] ScriptLoader.instance.object_map["IR_BALL"].body.velocity = (0.0, 0.0) for idx in range(len(self.bot_penalties)): if self.bot_penalties[idx] > 0: self.finishPenalty(idx)
def connectDevices(self): self.devices = {} for interactor in ScriptLoader.instance.object_map[ self.robot_key].device_interactors: self.devices[interactor.port] = interactor.device_class interactor.port_key = f"{self.filename}-{self.path_index}-{interactor.port}" Randomiser.createPortRandomiserWithSeed(interactor.port_key) ScriptLoader.instance.object_map[ self.robot_key].robot_class = self.robot_class
def _DistanceFromSensor(self, startPosition, centreRotation): top_length = self.MAX_RAYCAST while top_length > 0: endPosition = startPosition + top_length * np.array( [np.cos(centreRotation), np.sin(centreRotation)]) # Ignore all ignored objects by setting the category on them. cats = [] for obj in self.ignore_objects: if isinstance(obj, PhysicsObject): for shape in obj.shapes: cats.append(shape.filter.categories) shape.filter = pymunk.ShapeFilter(categories=0b1) raycast = World.instance.space.segment_query_first( [float(v) for v in startPosition], [float(v) for v in endPosition], self.RAYCAST_RADIUS, pymunk.ShapeFilter(mask=STATIC_CATEGORY | DYNAMIC_CATEGORY), ) i = 0 for obj in self.ignore_objects: if isinstance(obj, PhysicsObject): for shape in obj.shapes: shape.filter = pymunk.ShapeFilter(categories=cats[i]) i += 1 if raycast == None: if top_length == self.MAX_RAYCAST or ( not ScriptLoader.RANDOMISE_SENSORS): return max(0, min(self.MAX_RAYCAST, top_length + self.offset)) # If randomiser, linearly scale result by angle between surface normal of raycasted point. return max( 0, min( self.MAX_RAYCAST, top_length + (1 + (self.last_angle_diff + self.STATIC_RANDOM_ANGLE * Randomiser.random()) * (Randomiser.random() - 0.5) * self.ANGLE_RANDOM_AMPLITUDE / np.pi * 2) + self.offset, ), ) else: opposite_angle = centreRotation + np.pi while opposite_angle > raycast.normal.angle + np.pi: opposite_angle -= 2 * np.pi while opposite_angle < raycast.normal.angle - np.pi: opposite_angle += 2 * np.pi self.last_angle_diff = abs(opposite_angle - raycast.normal.angle) top_length = raycast.alpha * top_length - self.ACCEPTANCE_LEVEL return max(0, min(self.MAX_RAYCAST, self.offset))
def _SenseValueAboutPosition(self, centrePosition, valueGetter): # Randomly sample value from SENSOR_POINTS chosen around the centrePosition. points = [Randomiser.random() * self.SENSOR_RADIUS for _ in range(self.SENSOR_POINTS)] for x in range(len(points)): angle = Randomiser.random() * 2 * np.pi points[x] = valueGetter(np.array([np.cos(angle) * points[x], np.cos(angle) * points[x]]) + centrePosition) # For some reason the color module hangs otherwise :/ if hasattr(points[x], "r"): points[x] = np.array([points[x].r, points[x].g, points[x].b]) total = points[0] for x in range(1, len(points)): total += points[x] return total / len(points)
def generateBias(self): if ScriptLoader.RANDOMISE_SENSORS: # Distribute points cyclically between 0 and 360. self.dist = CompassValueDistribution( 0, 360, self.NEAREST_POINTS_NUMBER, self.NEAREST_POINTS_VARIANCE, Randomiser.getPortRandom(self._interactor.port_key), ) else: self.dist = CompassValueDistributionNoRandom(0, 360, 360) self.current_offset = 0 self.device_y_offset = self.NOISE_Y_OFFSET_MAX * self._interactor.random() self.noise_tick = 0 self._value = 0 self.noise = OpenSimplex(seed=Randomiser.getInstance().seeds[self._interactor.port_key]) self.calibrate()
def captureBotImage(self, directory, filename): self.resetVisualElements() from os.path import join from ev3sim.simulation.loader import ScriptLoader from ev3sim.robot import initialise_bot, RobotInteractor from ev3sim.simulation.randomisation import Randomiser Randomiser.createGlobalRandomiserWithSeed(0) ScriptLoader.instance.reset() ScriptLoader.instance.startUp() elems = {} initialise_bot(elems, find_abs(filename, [directory]), "", 0) ScriptLoader.instance.loadElements(elems.get("elements", [])) for interactor in ScriptLoader.instance.active_scripts: if isinstance(interactor, RobotInteractor): interactor.connectDevices() interactor.initialiseDevices() for interactor in ScriptLoader.instance.active_scripts: interactor.startUp() interactor.tick(0) interactor.afterPhysics() screen = pygame.Surface((480, 480), pygame.SRCALPHA) custom_map = { "SCREEN_WIDTH": 480, "SCREEN_HEIGHT": 480, "MAP_WIDTH": 25, "MAP_HEIGHT": 25, } for elem in self.objects.values(): elem.customMap = custom_map elem.calculatePoints() self.applyToScreen(screen, bg=pygame.Color(self.instance.background_colour)) colorkey = pygame.Color(self.instance.background_colour) for x in range(480): for y in range(480): val = screen.get_at((x, y)) val.a = 0 if (val.r == colorkey.r and val.g == colorkey.g and val.b == colorkey.b) else 255 screen.set_at((x, y), val) self.resetVisualElements() ScriptLoader.instance.reset() config_path = join(find_abs(filename, [directory]), "config.bot") with open(config_path, "r") as f: config = yaml.safe_load(f) pygame.image.save(screen, join(find_abs(filename, [directory]), config.get("preview_path", "preview.png")))
def resetPositions(self): for i, robot in enumerate(self.robots): diff_radius = Randomiser.random() * self.BOT_SPAWN_RADIUS diff_angle = Randomiser.random() * 2 * np.pi robot.body.position = [ self.BOT_SPAWN_POSITION[i][0][0] * self.TILE_LENGTH + diff_radius * np.cos(diff_angle), self.BOT_SPAWN_POSITION[i][0][1] * self.TILE_LENGTH + diff_radius * np.sin(diff_angle), ] robot.body.angle = ( ( self.BOT_SPAWN_POSITION[i][1] + self.BOT_SPAWN_ANGLE[0] + Randomiser.random() * (self.BOT_SPAWN_ANGLE[1] - self.BOT_SPAWN_ANGLE[0]) ) * np.pi / 180 ) robot.body.velocity = (0.0, 0.0) robot.body.angular_velocity = 0
def calibrate(self): """ Set the sensor so the current bearing is interpreted as 0. Example usage: ``` >>> compass.calibrate() >>> # Rotates 10 degrees to the left >>> compass.value() 10 ``` """ self._setRelative() self.current_offset = 0 if ScriptLoader.RANDOMISE_SENSORS: self.current_sample_point = Randomiser.random() * self.NOISE_SAMPLE_HEIGHT
def single_run(preset_filename, robots, bind_addr, seed, batch_file=None, override_settings={}): if batch_file: ScreenObjectManager.BATCH_FILE = batch_file ScreenObjectManager.PRESET_FILE = preset_filename import ev3sim try: latest_version = get_version_pypi("ev3sim") ScreenObjectManager.NEW_VERSION = latest_version != ev3sim.__version__ if ScreenObjectManager.NEW_VERSION: update_message = f"""\ ========================================================================================== There is a new version of ev3sim available ({latest_version}). Keeping an up to date version of ev3sim ensures you have the latest bugfixes and features. Please update ev3sim by running the following command: python -m pip install -U ev3sim ========================================================================================== """ print(update_message) except: ScreenObjectManager.NEW_VERSION = False Randomiser.createGlobalRandomiserWithSeed(seed) preset_file = find_abs(preset_filename, allowed_areas=[ "local", "local/presets/", "package", "package/presets/" ]) with open(preset_file, "r") as f: config = yaml.safe_load(f) config["robots"] = config.get("robots", []) + robots shared_data = { "tick": 0, # Current tick. "write_stack": deque(), # All write actions are processed through this. "data_queue": {}, # Simulation data for each bot. "active_count": {}, # Keeps track of which code connection each bot has. "bot_locks": {}, # Threading Locks and Conditions for each bot to wait for connection actions. "bot_communications_data": {}, # Buffers and information for all bot communications. "tick_updates": {}, # Simply a dictionary where the simulation tick will push static data, so the other methods are aware of when the simulation has exited. "events": {}, # Any events that should be sent to robots. } result_bucket = Queue(maxsize=1) from threading import Thread from ev3sim.simulation.communication import start_server_with_shared_data def run(shared_data, result): try: runFromConfig(config, shared_data) except Exception as e: result.put(("Simulation", e)) return result.put(True) # Handle any other settings modified by the preset. settings = config.get("settings", {}) settings.update(override_settings) for keyword, value in settings.items(): run = mock.patch(keyword, value)(run) comm_thread = Thread(target=start_server_with_shared_data, args=(shared_data, result_bucket, bind_addr), daemon=True) sim_thread = Thread(target=run, args=(shared_data, result_bucket), daemon=True) comm_thread.start() sim_thread.start() try: with result_bucket.not_empty: while not result_bucket._qsize(): result_bucket.not_empty.wait(0.1) r = result_bucket.get() # Chuck it back on the queue so that other threads know we are quitting. result_bucket.put(r) if r is not True: print( f"An error occurred in the {r[0]} thread. Raising an error now..." ) time.sleep(1) raise r[1] except KeyboardInterrupt: pass
def random(self): return Randomiser.getPortRandom(self.port_key).random()