diff --git a/.pre-commit-config.yml b/.pre-commit-config.yml index 820e518..736ebd2 100644 --- a/.pre-commit-config.yml +++ b/.pre-commit-config.yml @@ -2,19 +2,19 @@ default_language_version: python: python3.8 repos: - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort entry: bash -c 'isort --check-only .' stages: [commit] - repo: https://github.com/ambv/black - rev: 21.11b1 + rev: 23.9.1 hooks: - id: black entry: bash -c 'black --check .' stages: [commit] - repo: https://gitlab.com/pycqa/flake8 - rev: 4.0.1 + rev: 6.1.0 hooks: - id: flake8 entry: bash -c 'python3 -m flake8 .' diff --git a/CHANGELOG.md b/CHANGELOG.md index a54e09a..747e9be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Features + * **BREAKING** Change the message format sent between Emitter and Receiver + * Update Webots from R2022a to R2023b +* Other + * Update development requirements + ## v2.1 * Features diff --git a/README.md b/README.md index 920862c..44cb6ff 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ This is the official repository of the RoboCupJunior Soccer Simulator. The simulator is based on [Webots](https://github.com/cyberbotics/webots) and this repository provides both the "automatic referee" (which implements the [Soccer -Simulated Rules](https://github.com/RoboCupJuniorTC/soccer-rules-simulation)) +Simulated Rules](https://github.com/robocup-junior/soccer-rules-simulation)) as well as a sample simulated team of robots with some basic strategy. ![Soccer Sim](./docs/docs/images/soccer_sim.png) -*Learn more in the [documentation](https://robocupjuniortc.github.io/rcj-soccersim/).* +*Learn more in the [documentation](https://robocup-junior.github.io/rcj-soccersim/).* # How do I try this out? @@ -16,11 +16,11 @@ as well as a sample simulated team of robots with some basic strategy. 1. Install Python 3.7 (or higher) 64 bit from the [official website](https://www.python.org/downloads/) (please make sure it is version 3.7 or higher for Windows, and 3.8 or higher if installing on MacOS or Linux). On Windows, please make sure your Python is referenced in Windows PATH by selecting the option "Add Python 3.x to PATH" during the installation. Check out this great [installation guide](https://realpython.com/installing-python/) if you need some help! -2. Download [Webots](https://www.cyberbotics.com/#download) from their official website. Currently, version R2022a is stable with the Soccer Simulator. You can find detailed installation procedure on the official [Webots Installation guide](https://cyberbotics.com/doc/guide/installation-procedure). +2. Download [Webots](https://www.cyberbotics.com/#download) from their official website. Currently, version R2023b is stable with the Soccer Simulator. You can find detailed installation procedure on the official [Webots Installation guide](https://cyberbotics.com/doc/guide/installation-procedure). -3. Clone the rcj-soccersim repository to your computer by downloading the ZIP file from [here](https://github.com/RoboCupJuniorTC/rcj-soccersim/archive/master.zip) or running +3. Clone the rcj-soccersim repository to your computer by downloading the ZIP file from [here](https://github.com/robocup-junior/rcj-soccersim/archive/master.zip) or running - git clone https://github.com/RoboCupJuniorTC/rcj-soccersim.git + git clone https://github.com/robocup-junior/rcj-soccersim.git 4. Finally, run Webots, go to `Tools > Preferences > Python command` and set it to `python` or `python3` to point Webots to Python 3. Depending on your system, the reference to Python 3 can be via the command `python` or `python3`. More information on how to configure Webots to work with Python can be found [here](https://cyberbotics.com/doc/guide/using-python). @@ -48,7 +48,7 @@ avoid any compilation issues. ## Development -We are open to contributions! Have a look at our [issues](https://github.com/RoboCupJuniorTC/rcj-soccersim/issues). +We are open to contributions! Have a look at our [issues](https://github.com/robocup-junior/rcj-soccersim/issues). Before you make a pull request, make sure the code is formatted with `black` and `isort`, and `flake8` issues are fixed. diff --git a/controllers/rcj_soccer_ball/rcj_soccer_ball.py b/controllers/rcj_soccer_ball/rcj_soccer_ball.py index ee08ed6..edea09f 100644 --- a/controllers/rcj_soccer_ball/rcj_soccer_ball.py +++ b/controllers/rcj_soccer_ball/rcj_soccer_ball.py @@ -1,12 +1,9 @@ -import struct - from controller import Robot robot = Robot() ball_emitter = robot.getDevice("ball emitter") -data = [True] # Packet cannot be empty -packet = struct.pack("?", *data) +data = "x" # Packet cannot be empty while robot.step(32) != -1: - ball_emitter.send(packet) + ball_emitter.send(data) diff --git a/controllers/rcj_soccer_referee_supervisor/rcj_soccer_referee_supervisor.py b/controllers/rcj_soccer_referee_supervisor/rcj_soccer_referee_supervisor.py index b1c55e7..f5e75b8 100644 --- a/controllers/rcj_soccer_referee_supervisor/rcj_soccer_referee_supervisor.py +++ b/controllers/rcj_soccer_referee_supervisor/rcj_soccer_referee_supervisor.py @@ -30,7 +30,6 @@ def output_path( match_id: int, half_id: int, ) -> PosixPath: - now_str = datetime.utcnow().strftime("%Y%m%dT%H%M%S") team_blue = team_blue_id.replace(" ", "_") team_yellow = team_yellow_id.replace(" ", "_") diff --git a/controllers/rcj_soccer_referee_supervisor/recorder/recorder.py b/controllers/rcj_soccer_referee_supervisor/recorder/recorder.py index 8e48927..6101567 100644 --- a/controllers/rcj_soccer_referee_supervisor/recorder/recorder.py +++ b/controllers/rcj_soccer_referee_supervisor/recorder/recorder.py @@ -17,7 +17,6 @@ def __init__( fastforward_rate: int = 1, resolution: str = "720p", ): - self.supervisor = supervisor self.output_path = output_path self.fastforward_rate = fastforward_rate diff --git a/controllers/rcj_soccer_referee_supervisor/referee/progress_checker.py b/controllers/rcj_soccer_referee_supervisor/referee/progress_checker.py index 971ea13..720a14a 100644 --- a/controllers/rcj_soccer_referee_supervisor/referee/progress_checker.py +++ b/controllers/rcj_soccer_referee_supervisor/referee/progress_checker.py @@ -15,10 +15,10 @@ def reset(self): self.prev_position = None def track(self, position: List[float]): - """Make ProgressChecker react to a new position. Internally, it computes - the Euclidian distance from the previous position and saves it so that - it can be used when computing whether the given object has made - progress. + """Make ProgressChecker react to a new position. Internally, it + computes the Euclidian distance from the previous position and + saves it so that it can be used when computing whether the given + object has made progress. Args: position (list): Current position of the object diff --git a/controllers/rcj_soccer_referee_supervisor/referee/referee.py b/controllers/rcj_soccer_referee_supervisor/referee/referee.py index a293f15..2b05899 100644 --- a/controllers/rcj_soccer_referee_supervisor/referee/referee.py +++ b/controllers/rcj_soccer_referee_supervisor/referee/referee.py @@ -1,5 +1,5 @@ +import json import random -import struct from typing import List, Optional, Tuple from controller import Supervisor @@ -91,21 +91,18 @@ def __init__( self.sv.draw_team_names(self.team_name_blue, self.team_name_yellow) self.sv.draw_scores(self.score_blue, self.score_yellow) - def _pack_packet(self) -> bytes: - """Pack data into packet. + def _pack_data(self) -> str: + """Pack data into json string. Returns: - bytes: the packed packet. + str: json data encoded into string. """ - # True/False telling whether the goal was scored - struct_fmt = "?" - data = list() + # Add Notification if the goal is scored and we are + # waiting for kickoff. The value is True or False + waiting_for_kickoff = self.ball_reset_timer > 0 - # Add Notification if the goal is scored and we are waiting for kickoff - # The value is True or False - data.append(self.ball_reset_timer > 0) - - return struct.pack(struct_fmt, *data) + data = {"waiting_for_kickoff": waiting_for_kickoff} + return json.dumps(data) def _add_initial_position_noise( self, translation: List[float] @@ -396,7 +393,7 @@ def tick(self) -> bool: ) self.sv.update_positions() - self.sv.emit_data(self._pack_packet()) + self.sv.emit_data(self._pack_data()) self.time -= TIME_STEP / 1000.0 # On the very last tick, note that the match has finished diff --git a/controllers/rcj_soccer_referee_supervisor/referee/supervisor.py b/controllers/rcj_soccer_referee_supervisor/referee/supervisor.py index 4fe9bb5..f1771d8 100644 --- a/controllers/rcj_soccer_referee_supervisor/referee/supervisor.py +++ b/controllers/rcj_soccer_referee_supervisor/referee/supervisor.py @@ -217,13 +217,13 @@ def move_object_to_neutral_spot(self, object_name: str, neutral_spot: str): object_name, ROBOT_INITIAL_ROTATION[object_name] ) - def emit_data(self, packet: bytes): + def emit_data(self, data: str): """Send packet via emitter Args: - packet (bytes): the packet to be sent + data (str): the data to be sent """ - self.emitter.send(packet) + self.emitter.send(data) def draw_team_names(self, team_name_blue: str, team_name_yellow: str): """Visualize (draw) the names of the teams. diff --git a/controllers/rcj_soccer_referee_supervisor/referee/tests/test_referee.py b/controllers/rcj_soccer_referee_supervisor/referee/tests/test_referee.py index 72e13bb..d995fc0 100644 --- a/controllers/rcj_soccer_referee_supervisor/referee/tests/test_referee.py +++ b/controllers/rcj_soccer_referee_supervisor/referee/tests/test_referee.py @@ -32,7 +32,7 @@ def referee() -> RCJSoccerReferee: def test_pack_packet(referee: RCJSoccerReferee): - assert referee._pack_packet() == b"\x00" + assert referee._pack_data() == '{"waiting_for_kickoff": false}' def test_add_initial_position_noise(referee: RCJSoccerReferee): diff --git a/controllers/rcj_soccer_team_blue/rcj_soccer_robot.py b/controllers/rcj_soccer_team_blue/rcj_soccer_robot.py index 741b1ee..0826c2e 100644 --- a/controllers/rcj_soccer_team_blue/rcj_soccer_robot.py +++ b/controllers/rcj_soccer_team_blue/rcj_soccer_robot.py @@ -1,5 +1,5 @@ +import json import math -import struct TIME_STEP = 32 ROBOT_NAMES = ["B1", "B2", "B3", "Y1", "Y2", "Y3"] @@ -47,22 +47,20 @@ def __init__(self, robot): self.left_motor.setVelocity(0.0) self.right_motor.setVelocity(0.0) - def parse_supervisor_msg(self, packet: str) -> dict: + def parse_supervisor_msg(self, data: str) -> dict: """Parse message received from supervisor + Args: + data: json data encoded into string + Returns: - dict: Location info about each robot and the ball. + dict: data decoded into dictionary Example: { 'waiting_for_kickoff': False, } """ - # True/False telling whether the goal was scored - struct_fmt = "?" - unpacked = struct.unpack(struct_fmt, packet) - - data = {"waiting_for_kickoff": unpacked[0]} - return data + return json.loads(data) def get_new_data(self) -> dict: """Read new data from supervisor @@ -70,10 +68,9 @@ def get_new_data(self) -> dict: Returns: dict: See `parse_supervisor_msg` method """ - packet = self.receiver.getData() + data = self.receiver.getString() self.receiver.nextPacket() - - return self.parse_supervisor_msg(packet) + return self.parse_supervisor_msg(data) def is_new_data(self) -> bool: """Check if there is new data from supervisor to be received @@ -83,18 +80,16 @@ def is_new_data(self) -> bool: """ return self.receiver.getQueueLength() > 0 - def parse_team_msg(self, packet: str) -> dict: + def parse_team_msg(self, data: str) -> dict: """Parse message received from team robot + Args: + dict: json data encoded into string + Returns: - dict: Parsed message stored in dictionary. + dict: data decoded into dictionary """ - struct_fmt = "i" - unpacked = struct.unpack(struct_fmt, packet) - data = { - "robot_id": unpacked[0], - } - return data + return json.loads(data) def get_new_team_data(self) -> dict: """Read new data from team robot @@ -102,9 +97,9 @@ def get_new_team_data(self) -> dict: Returns: dict: See `parse_team_msg` method """ - packet = self.team_receiver.getData() + data = self.team_receiver.getString() self.team_receiver.nextPacket() - return self.parse_team_msg(packet) + return self.parse_team_msg(data) def is_new_team_data(self) -> bool: """Check if there is new data from team robots to be received @@ -114,16 +109,14 @@ def is_new_team_data(self) -> bool: """ return self.team_receiver.getQueueLength() > 0 - def send_data_to_team(self, robot_id) -> None: + def send_data_to_team(self, robot_id: int) -> None: """Send data to the team Args: robot_id (int): ID of the robot """ - struct_fmt = "i" - data = [robot_id] - packet = struct.pack(struct_fmt, *data) - self.team_emitter.send(packet) + data = {"robot_id": robot_id} + self.team_emitter.send(json.dumps(data)) def get_new_ball_data(self) -> dict: """Read new data from IR sensor @@ -138,9 +131,12 @@ def get_new_ball_data(self) -> dict: 'strength': 0.1 } """ - _ = self.ball_receiver.getData() + _ = self.ball_receiver.getString() data = { - "direction": self.ball_receiver.getEmitterDirection(), + # TODO: Remove [:3] once + # https://github.com/cyberbotics/webots/pull/6394 + # is released and Webots updated + "direction": self.ball_receiver.getEmitterDirection()[:3], "strength": self.ball_receiver.getSignalStrength(), } self.ball_receiver.nextPacket() diff --git a/controllers/rcj_soccer_team_yellow/rcj_soccer_robot.py b/controllers/rcj_soccer_team_yellow/rcj_soccer_robot.py index 417ca09..aa5eca0 100644 --- a/controllers/rcj_soccer_team_yellow/rcj_soccer_robot.py +++ b/controllers/rcj_soccer_team_yellow/rcj_soccer_robot.py @@ -1,5 +1,5 @@ +import json import math -import struct TIME_STEP = 32 ROBOT_NAMES = ["B1", "B2", "B3", "Y1", "Y2", "Y3"] @@ -47,22 +47,20 @@ def __init__(self, robot): self.left_motor.setVelocity(0.0) self.right_motor.setVelocity(0.0) - def parse_supervisor_msg(self, packet: str) -> dict: + def parse_supervisor_msg(self, data: str) -> dict: """Parse message received from supervisor + Args: + data: json data encoded into string + Returns: - dict: Location info about each robot and the ball. + dict: data decoded into dictionary Example: { 'waiting_for_kickoff': False, } """ - # True/False telling whether the goal was scored - struct_fmt = "?" - unpacked = struct.unpack(struct_fmt, packet) - - data = {"waiting_for_kickoff": unpacked[0]} - return data + return json.loads(data) def get_new_data(self) -> dict: """Read new data from supervisor @@ -70,9 +68,9 @@ def get_new_data(self) -> dict: Returns: dict: See `parse_supervisor_msg` method """ - packet = self.receiver.getData() + data = self.receiver.getString() self.receiver.nextPacket() - return self.parse_supervisor_msg(packet) + return self.parse_supervisor_msg(data) def is_new_data(self) -> bool: """Check if there is new data from supervisor to be received @@ -82,18 +80,16 @@ def is_new_data(self) -> bool: """ return self.receiver.getQueueLength() > 0 - def parse_team_msg(self, packet: str) -> dict: + def parse_team_msg(self, data: str) -> dict: """Parse message received from team robot + Args: + dict: json data encoded into string + Returns: - dict: Parsed message stored in dictionary. + dict: data decoded into dictionary """ - struct_fmt = "i" - unpacked = struct.unpack(struct_fmt, packet) - data = { - "robot_id": unpacked[0], - } - return data + return json.loads(data) def get_new_team_data(self) -> dict: """Read new data from team robot @@ -101,9 +97,9 @@ def get_new_team_data(self) -> dict: Returns: dict: See `parse_team_msg` method """ - packet = self.team_receiver.getData() + data = self.team_receiver.getString() self.team_receiver.nextPacket() - return self.parse_team_msg(packet) + return self.parse_team_msg(data) def is_new_team_data(self) -> bool: """Check if there is new data from team robots to be received @@ -113,16 +109,14 @@ def is_new_team_data(self) -> bool: """ return self.team_receiver.getQueueLength() > 0 - def send_data_to_team(self, robot_id) -> None: + def send_data_to_team(self, robot_id: int) -> None: """Send data to the team Args: robot_id (int): ID of the robot """ - struct_fmt = "i" - data = [robot_id] - packet = struct.pack(struct_fmt, *data) - self.team_emitter.send(packet) + data = {"robot_id": robot_id} + self.team_emitter.send(json.dumps(data)) def get_new_ball_data(self) -> dict: """Read new data from IR sensor @@ -137,9 +131,12 @@ def get_new_ball_data(self) -> dict: 'strength': 0.1 } """ - _ = self.ball_receiver.getData() + _ = self.ball_receiver.getString() data = { - "direction": self.ball_receiver.getEmitterDirection(), + # TODO: Remove [:3] once + # https://github.com/cyberbotics/webots/pull/6394 + # is released and Webots updated + "direction": self.ball_receiver.getEmitterDirection()[:3], "strength": self.ball_receiver.getSignalStrength(), } self.ball_receiver.nextPacket() diff --git a/docs/docs/communication_between_robots.md b/docs/docs/communication_between_robots.md index c2292b2..631cef2 100644 --- a/docs/docs/communication_between_robots.md +++ b/docs/docs/communication_between_robots.md @@ -38,38 +38,33 @@ this channel are going to receive it. So, if `robot1` sends a message, ### Converting the message into a packet -Imagine we would like to send values `v1`, `v2`, ... , `vn`. -Before the message is sent to the channel, it must be converted into a packet. -Simply put, the packet is just a bytes object representing values `v1`, `v2`, ... , `vn`. -To convert our values into bytes object we can use built-in library -called [struct](https://docs.python.org/3/library/struct.html), which is -shipped together with Python. - -We need to know the type of each value we want to pack into a packet. Let's see a -quick example. The values we want to send are `v1 = 3.14`, `v2 = 5` and `v3 = True`. -It is obvious that `v1` is floating point number, `v2` is integer and `v3` -is boolean. Knowing the type of each variable, we need to define the -structure of the message. +Webots supports sending the packet in different formats (string, bytes, list of doubles...). +Since our example controllers as well as supervisor use strings, let's describe how to send it. + +[JSON](https://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) is an +open standard file format and data interchange format that uses human-readable +text to store and transmit data objects consisting of attribute–value pairs and +arrays (or other serializable values). +In short, it is very similar to the `dict` data structure that Python uses. +Starting from version 3.0, Python contains a built-in library called [json](https://docs.python.org/3/library/json.html). It +offers encoding Python's dictionaries into JSON string and later decoding it back to a dictionary. +This is very useful in our case because by reading the keys the receiver already knows what the +value means. + +Let's create a simple message that we would like to send: ```python -message_format = "di?" +data = {"robot_id": 1, "my_array": [1, 1.0, 2]} ``` -The `struct` library defines various symbols we might use in order to represent -a variable type. In our example `"d"` is representing floating point number, -`"i"` is representing integer and `"?"` is representing boolean value. -*If you want to use other variable types, check out -[the struct format characters documentation](https://docs.python.org/3/library/struct.html#format-characters)*. -Since we now know the structure of the message, we can create the packet +Before sending the data, we must convert it to the string so the emitter is able to +encode it. As mentioned above, `json` library contains a method `dumps` that takes care +of that. ```python -packet = struct.pack(message_format, v1, v2, v3) +packet = json.dumps(data) ``` -**Keep in mind that the order of variables passed to `pack()` function MUST be -in the same order as specified in `message_format`, otherwise you might get -a broken packet.** - The only thing we need to do is emit the packet by the Emitter. It is as easy as just calling @@ -79,7 +74,6 @@ self.team_emitter.send(packet) and the message is succesfully sent to the channel. - ### Receiving a message When a Receiver receives a message, it adds it to the queue. We are then @@ -95,41 +89,24 @@ If the there are new messages, the number of messages will be greater than 0 and we can proceed with reading the message. ```python -packet = self.team_receiver.getData() +packet = self.team_receiver.getString() self.team_receiver.nextPacket() ``` -In the first row we call the `getData()` method, which returns the packet and assigns +In the first row we call the `getString()` method, which returns the packet and assigns it to the `packet` variable. The `nextPacket()` method is used to move the pointer -to the next packet in the queue, so next time we are calling `getData()`, we are going +to the next packet in the queue, so next time we are calling `getString()`, we are going to read next packet in the queue. -Okay, now we have packet. How do we unpack it? Well, remember our cool `struct` -library? We are going to use it for unpacking the packet, too. - -First of all, we need to know what data we expect. In our example, we sent -three variables in this order - floating point number, integer and boolean. Therefore, -we can define the format of the message here as well. +Okay, now we have packet. How do we decode it? Well, remember our cool `json` +library? We are going to use it for decoding the packet, too. ```python -message_format = 'di?' +data = json.loads(packet) ``` -Knowing what data to expect, we can unpack the packet by following command: - -```python -unpacked = struct.unpack(message_format, packet) -``` - -The variable `unpacked` is a tuple with our values. If we print it, we will get -`(3.14, 5, True)`. We can access the values as we would for list -(or array in other languages) - -```python -v1 = unpacked[0] -v2 = unpacked[1] -v3 = unpacked[2] -``` +The variable `data` now contains the same dictionary we sent by the emitter and can be +accessed as normal dictionary object. **WARNING: There are 3 robots within the team running asynchronously. If all of them @@ -139,7 +116,11 @@ a `while` loop for example** ```python while self.team_receiver.getQueueLength() > 0: - packet = self.team_receiver.getData() + packet = self.team_receiver.getString() self.team_receiver.nextPacket() # Do something with the packet ``` + +**WARNING: Webots does not guarantee the order of messages. Your robot controllers should not rely on the order. +Instead, we recommend sending robot identifier in the message payload so the receiving robots clearly know which robot +originally sent the message.** diff --git a/docs/docs/getting_started.md b/docs/docs/getting_started.md index e6fd176..6148832 100644 --- a/docs/docs/getting_started.md +++ b/docs/docs/getting_started.md @@ -8,11 +8,11 @@ It's easy, you can set it up in about 10 minutes! 1. Install Python 3.7 (or higher) 64 bit from the [official website](https://www.python.org/downloads/) (please make sure it is version 3.7 or higher for Windows, and 3.8 or higher if installing on MacOS or Linux). On Windows, please make sure your Python is referenced in Windows PATH by selecting the option "Add Python 3.x to PATH" during the installation. Check out this great [installation guide](https://realpython.com/installing-python/) if you need some help! -2. Download [Webots](https://www.cyberbotics.com/#download) from their official website. Currently, version R2022a is stable with the Soccer Simulator. You can find detailed installation procedure on the official [Webots Installation guide](https://cyberbotics.com/doc/guide/installation-procedure). +2. Download [Webots](https://www.cyberbotics.com/#download) from their official website. Currently, version R2023b is stable with the Soccer Simulator. You can find detailed installation procedure on the official [Webots Installation guide](https://cyberbotics.com/doc/guide/installation-procedure). -3. Clone the rcj-soccersim repository to your computer by downloading the ZIP file from [here](https://github.com/RoboCupJuniorTC/rcj-soccersim/archive/master.zip) or running +3. Clone the rcj-soccersim repository to your computer by downloading the ZIP file from [here](https://github.com/robocup-junior/rcj-soccersim/archive/master.zip) or running - git clone https://github.com/RoboCupJuniorTC/rcj-soccersim.git + git clone https://github.com/robocup-junior/rcj-soccersim.git 4. Finally, run Webots, go to `Tools > Preferences > Python command` and set it to `python` or `python3` to point Webots to Python 3. Depending on your system, the reference to Python 3 can be via the command `python` or `python3`. More information on how to configure Webots to work with Python can be found [here](https://cyberbotics.com/doc/guide/using-python). diff --git a/docs/docs/how_to_robot.md b/docs/docs/how_to_robot.md index 9d63ac1..7cc0d1d 100644 --- a/docs/docs/how_to_robot.md +++ b/docs/docs/how_to_robot.md @@ -100,7 +100,7 @@ robot we initialized previously. Let's put together a simple program to showcase how you can go about programming a robot. ```python -import struct +import json TIME_STEP = 32 ROBOT_NAMES = ["B1", "B2", "B3", "Y1", "Y2", "Y3"] @@ -143,16 +143,9 @@ class MyRobot: self.right_motor.setVelocity(0.0) def get_new_data(self): - packet = self.receiver.getData() + packet = self.receiver.getString() self.receiver.nextPacket() - - struct_fmt = '?' - - unpacked = struct.unpack(struct_fmt, packet) - data = { - "waiting_for_kickoff": unpacked[0] - } - return data + return json.loads(packet) def run(self): while self.robot.step(TIME_STEP) != -1: @@ -179,11 +172,11 @@ class MyRobot: Let's explain the code in detail: ```python -import struct +import json ``` -This library is a [built-in Python library](https://docs.python.org/3/library/struct.html), -which is required to unpack the data sent by the supervisor. +This library is a [built-in Python library](https://docs.python.org/3/library/json.html), +which is required to decode the data sent by the supervisor. ```python TIME_STEP = 32 @@ -225,7 +218,7 @@ def get_new_data(self): ... ``` -We are not going to explain this deeply. This function simply parses the incoming +We are not going to explain this deeply. This function simply decodes the incoming data from supervisor. Feel free to copy and use it. The resulting dictionary just contains a single bit of information: whether a goal was scored and we are waiting for a new kickoff. In case the goal gets scored, the value is `True` and is reset to `False` when the diff --git a/docs/docs/index.md b/docs/docs/index.md index 11ea3b8..265d09a 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -3,7 +3,7 @@ This is the official repository of the RoboCupJunior Soccer Simulator. The simulator is based on [Webots](https://github.com/cyberbotics/webots) and this repository provides both the "automatic referee" (which implements the [Soccer -Simulated Rules](https://github.com/RoboCupJuniorTC/soccer-rules-simulation)) +Simulated Rules](https://github.com/robocup-junior/soccer-rules-simulation)) as well as a sample simulated team of robots with some basic strategy. diff --git a/requirements/development.txt b/requirements/development.txt index 0493f35..16ee6f2 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -4,9 +4,7 @@ # # pip-compile development.in # -attrs==21.4.0 - # via pytest -black==21.12b0 +black==23.9.1 # via -r development.in cfgv==3.3.1 # via pre-commit @@ -18,24 +16,28 @@ coverage[toml]==6.2 # via pytest-cov distlib==0.3.4 # via virtualenv +exceptiongroup==1.1.3 + # via pytest filelock==3.4.2 # via virtualenv -flake8==4.0.1 +flake8==6.1.0 # via -r development.in identify==2.4.3 # via pre-commit iniconfig==1.1.1 # via pytest -isort==5.10.1 +isort==5.12.0 # via -r development.in -mccabe==0.6.1 +mccabe==0.7.0 # via flake8 mypy-extensions==0.4.3 # via black nodeenv==1.6.0 # via pre-commit -packaging==21.3 - # via pytest +packaging==23.1 + # via + # black + # pytest pathspec==0.9.0 # via black pep517==0.12.0 @@ -48,35 +50,28 @@ platformdirs==2.4.1 # virtualenv pluggy==1.0.0 # via pytest -pre-commit==2.16.0 +pre-commit==3.4.0 # via -r development.in -py==1.11.0 - # via pytest -pycodestyle==2.8.0 +pycodestyle==2.11.0 # via flake8 -pyflakes==2.4.0 +pyflakes==3.1.0 # via flake8 -pyparsing==3.0.6 - # via packaging -pytest==6.2.5 +pytest==7.4.2 # via # -r development.in # pytest-cov -pytest-cov==3.0.0 +pytest-cov==4.1.0 # via -r development.in pyyaml==6.0 # via pre-commit six==1.16.0 # via virtualenv -toml==0.10.2 - # via - # pre-commit - # pytest tomli==1.2.3 # via # black # coverage # pep517 + # pytest typing-extensions==4.0.1 # via black virtualenv==20.13.0 diff --git a/worlds/soccer.wbt b/worlds/soccer.wbt index 335807c..41e1b3d 100644 --- a/worlds/soccer.wbt +++ b/worlds/soccer.wbt @@ -1,4 +1,8 @@ -#VRML_SIM R2022a utf8 +#VRML_SIM R2023b utf8 + +EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2022b/projects/objects/backgrounds/protos/TexturedBackground.proto" +EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2022b/projects/objects/backgrounds/protos/TexturedBackgroundLight.proto" + WorldInfo { info [ "A simple RCJ Soccer game simulated using Webots." @@ -16,7 +20,7 @@ TexturedBackgroundLight { } DEF SOCCER_FIELD Solid { children [ - DEF GROUND Transform { + DEF GROUND Pose { rotation 0 0 1 1.5707996938995747 children [ Shape { @@ -251,7 +255,7 @@ DEF SOCCER_FIELD Solid { boundingObject Group { children [ USE GROUND - DEF NOTH_BBOX Transform { + DEF NOTH_BBOX Pose { translation 0.655 0 0.045 children [ Box { @@ -259,7 +263,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF SOUTH_BBOX Transform { + DEF SOUTH_BBOX Pose { translation -0.655 0 0.045 children [ Box { @@ -267,7 +271,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF NW_BBOX Transform { + DEF NW_BBOX Pose { translation 0.43 0.755 0.045 children [ Box { @@ -275,7 +279,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF NE_BBOX Transform { + DEF NE_BBOX Pose { translation 0.43 -0.755 0.045 children [ Box { @@ -283,7 +287,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF SW_BBOX Transform { + DEF SW_BBOX Pose { translation -0.43 0.755 0.045 children [ Box { @@ -291,7 +295,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF SE_BBOX Transform { + DEF SE_BBOX Pose { translation -0.43 -0.755 0.045 children [ Box { @@ -299,7 +303,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF NWC_BBOX Transform { + DEF NWC_BBOX Pose { translation -0.62 -0.725 0.045 rotation 0 0 1 -0.7853 children [ @@ -308,7 +312,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF NEC_BBOX Transform { + DEF NEC_BBOX Pose { translation -0.62 0.725 0.045 rotation 0 0 1 0.7853 children [ @@ -317,7 +321,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF SWC_BBOX Transform { + DEF SWC_BBOX Pose { translation 0.62 -0.725 0.045 rotation 0 0 1 0.7853 children [ @@ -326,7 +330,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF SEC_BBOX Transform { + DEF SEC_BBOX Pose { translation 0.62 0.725 0.045 rotation 0 0 1 -0.7853 children [ @@ -335,7 +339,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF YG_BBOX Transform { + DEF YG_BBOX Pose { translation 0 0.855 0.061 children [ Box { @@ -343,7 +347,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF BG_BBOX Transform { + DEF BG_BBOX Pose { translation 0 -0.855 0.061 children [ Box { @@ -351,7 +355,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF BGS_BBOX Transform { + DEF BGS_BBOX Pose { translation -0.205 -0.8 0.061 children [ Box { @@ -359,7 +363,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF BGN_BBOX Transform { + DEF BGN_BBOX Pose { translation 0.205 -0.8 0.061 children [ Box { @@ -367,7 +371,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF YGS_BBOX Transform { + DEF YGS_BBOX Pose { translation -0.205 0.8 0.061 children [ Box { @@ -375,7 +379,7 @@ DEF SOCCER_FIELD Solid { } ] } - DEF YGN_BBOX Transform { + DEF YGN_BBOX Pose { translation 0.205 0.8 0.061 children [ Box { @@ -458,7 +462,7 @@ DEF B1 Robot { translation 0 -0.045 -0.0176 rotation -3.337240149329015e-15 -6.254334002834076e-15 1 1.5707999999999998 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ DEF WHEEL_SHAPE Shape { @@ -475,7 +479,7 @@ DEF B1 Robot { } ] name "right wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ USE WHEEL_SHAPE @@ -505,7 +509,7 @@ DEF B1 Robot { translation 0 0.045 -0.0176 rotation -6.410229215105164e-15 -3.362702725970239e-15 1 1.5707996938995745 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -522,7 +526,7 @@ DEF B1 Robot { } ] name "left wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -720,7 +724,7 @@ DEF B2 Robot { translation 0 -0.045 -0.0176 rotation -3.337240149329015e-15 -6.254334002834076e-15 1 1.5707999999999998 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ DEF WHEEL_SHAPE Shape { @@ -737,7 +741,7 @@ DEF B2 Robot { } ] name "right wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ USE WHEEL_SHAPE @@ -767,7 +771,7 @@ DEF B2 Robot { translation 0 0.045 -0.0176 rotation -6.410229215105164e-15 -3.362702725970239e-15 1 1.5707996938995745 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -784,7 +788,7 @@ DEF B2 Robot { } ] name "left wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -979,7 +983,7 @@ DEF B3 Robot { translation 0 -0.045 -0.0176 rotation -3.337240149329015e-15 -6.254334002834076e-15 1 1.5707999999999998 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ DEF WHEEL_SHAPE Shape { @@ -996,7 +1000,7 @@ DEF B3 Robot { } ] name "right wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ USE WHEEL_SHAPE @@ -1026,7 +1030,7 @@ DEF B3 Robot { translation 0 0.045 -0.0176 rotation -6.410229215105164e-15 -3.362702725970239e-15 1 1.5707996938995745 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -1043,7 +1047,7 @@ DEF B3 Robot { } ] name "left wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -1236,9 +1240,9 @@ DEF Y1 Robot { ] endPoint Solid { translation 0 -0.045 -0.0176 - rotation -3.9627690133025035e-11 3.9627544572658754e-11 -1 1.5708 + rotation -4.84338434959195e-11 4.843366558880515e-11 -1 1.5708 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ DEF WHEEL_SHAPE Shape { @@ -1255,7 +1259,7 @@ DEF Y1 Robot { } ] name "right wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ USE WHEEL_SHAPE @@ -1283,9 +1287,9 @@ DEF Y1 Robot { ] endPoint Solid { translation 0 0.045 -0.0176 - rotation -3.876481307781522e-11 3.876467068696751e-11 -1 1.5708 + rotation -4.7379215983996383e-11 4.737904195073807e-11 -1 1.5708 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -1302,7 +1306,7 @@ DEF Y1 Robot { } ] name "left wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -1495,9 +1499,9 @@ DEF Y2 Robot { ] endPoint Solid { translation 0 -0.045 -0.0176 - rotation -3.020494264136286e-11 3.020483169261717e-11 -1 1.5708 + rotation -3.8834926253180834e-11 3.883478360479352e-11 -1 1.5708 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ DEF WHEEL_SHAPE Shape { @@ -1514,7 +1518,7 @@ DEF Y2 Robot { } ] name "right wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ USE WHEEL_SHAPE @@ -1542,9 +1546,9 @@ DEF Y2 Robot { ] endPoint Solid { translation 0 0.045 -0.0176 - rotation -3.055546077846123e-11 3.055534854219291e-11 -1 1.5708 + rotation -3.928559242945016e-11 3.92854481256766e-11 -1 1.5708 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -1561,7 +1565,7 @@ DEF Y2 Robot { } ] name "left wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -1754,9 +1758,9 @@ DEF Y3 Robot { ] endPoint Solid { translation 0 -0.045 -0.0176 - rotation 2.2826629475503072e-11 -2.2826545628765183e-11 -1 1.5708 + rotation 2.9348523611361095e-11 -2.934841580841238e-11 -1 1.5708 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ DEF WHEEL_SHAPE Shape { @@ -1773,7 +1777,7 @@ DEF Y3 Robot { } ] name "right wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ USE WHEEL_SHAPE @@ -1801,9 +1805,9 @@ DEF Y3 Robot { ] endPoint Solid { translation 0 0.045 -0.0176 - rotation 2.2798651855282515e-11 -2.2798568111311978e-11 -1 1.5708 + rotation 2.931255238536323e-11 -2.9312444714543974e-11 -1 1.5708 children [ - Transform { + Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape { @@ -1820,7 +1824,7 @@ DEF Y3 Robot { } ] name "left wheel" - boundingObject Transform { + boundingObject Pose { rotation -0.5771969549958951 0.5776567755050862 -0.5771969549958951 2.0939354039397986 children [ Shape {