-
Notifications
You must be signed in to change notification settings - Fork 0
decouple pull and send robot data in real and drop timeout #106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
energy-in-joles
wants to merge
7
commits into
main
Choose a base branch
from
feat/decouple_pull_and_send_robot_data
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
a4e4af8
decouple pull and send robot data in real and drop timeout
energy-in-joles dcd5502
Merge branch 'main' into feat/decouple_pull_and_send_robot_data
energy-in-joles 0080884
add rx buffer to manage partial reads
energy-in-joles 00182bf
fully remove timeout
energy-in-joles 439254d
add get_robot_response for when its implemented in hardware
energy-in-joles 79989bc
Merge branch 'main' into feat/decouple_pull_and_send_robot_data
energy-in-joles e875cdd
make it non-blocking even on partial read
energy-in-joles File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,6 @@ | |
| KICKER_COOLDOWN_TIMESTEPS, | ||
| KICKER_PERSIST_TIMESTEPS, | ||
| PORT, | ||
| TIMEOUT, | ||
| TIMESTEP, | ||
| ) | ||
| from utama_core.entities.data.command import RobotCommand, RobotResponse | ||
|
|
@@ -24,7 +23,6 @@ | |
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| # NB: A major assumption is that the robot IDs are 0-5 for the friendly team. | ||
| MAX_VEL = REAL_PARAMS.MAX_VEL | ||
| MAX_ANGULAR_VEL = REAL_PARAMS.MAX_ANGULAR_VEL | ||
|
|
||
|
|
@@ -44,23 +42,59 @@ class RealRobotController(AbstractRobotController): | |
| n_robots (int): The number of robots in the team. Directly affects output buffer size. Default is 6. | ||
| """ | ||
|
|
||
| HEADER = 0xAA | ||
| TAIL = 0x55 | ||
|
|
||
| def __init__(self, is_team_yellow: bool, n_friendly: int): | ||
| super().__init__(is_team_yellow, n_friendly) | ||
| self._serial_port = self._init_serial() | ||
| self._rbt_cmd_size = 10 # packet size for one robot | ||
| self._rx_buffer = bytearray() | ||
| self._rbt_cmd_size = 10 # packet size (bytes) for one robot | ||
| self._robot_info_size = 2 # number of bytes in feedback packet for all robots | ||
| self._out_packet = self._empty_command() | ||
| self._in_packet_size = 1 # size of the feedback packet received from the robots | ||
| self._robots_info: List[RobotResponse] = [None] * self._n_friendly | ||
| logger.debug(f"Serial port: {PORT} opened with baudrate: {BAUD_RATE} and timeout {TIMEOUT}") | ||
| logger.debug(f"Serial port: {PORT} opened with baudrate: {BAUD_RATE}.") | ||
| self._assigned_mapping = {} # mapping of robot_id to index in the out_packet | ||
|
|
||
| # track last kick time for each robot to transmit kick as HIGH for n timesteps after command | ||
| self._kicker_tracker: Dict[int, KickTrackerEntry] = {} | ||
|
|
||
| def get_robots_responses(self) -> Optional[List[RobotResponse]]: | ||
| ### TODO: Not implemented yet | ||
| def get_robots_responses(self) -> Optional[list[RobotResponse]]: | ||
| # For now, we are not processing feedback from the robots, but this method can be implemented similarly to the GRSimRobotController if needed in the future. | ||
| self._serial_port.read_all() # Clear the input buffer to avoid overflow | ||
| return None | ||
|
energy-in-joles marked this conversation as resolved.
|
||
|
|
||
| # TODO: implement when feedback is actually working | ||
| def get_robots_responses_real(self) -> Optional[list[RobotResponse]]: | ||
| FRAME_SIZE = 1 + self._robot_info_size + 1 | ||
|
|
||
| # --- Step 1: Non-blocking read --- | ||
| waiting = self._serial_port.in_waiting | ||
| if waiting: | ||
| self._rx_buffer.extend(self._serial_port.read(waiting)) | ||
|
|
||
| latest_payload = None | ||
|
|
||
| # --- Step 2: Forward scan for complete frames --- | ||
| while len(self._rx_buffer) >= FRAME_SIZE: | ||
| if self._rx_buffer[0] != self.HEADER: | ||
| next_header = self._rx_buffer.find(bytes([self.HEADER])) | ||
| if next_header == -1: | ||
| self._rx_buffer.clear() | ||
| break | ||
| self._rx_buffer = self._rx_buffer[next_header:] | ||
| continue | ||
|
|
||
| if self._rx_buffer[FRAME_SIZE - 1] == self.TAIL: | ||
| latest_payload = self._rx_buffer[1 : FRAME_SIZE - 1] | ||
| self._rx_buffer = self._rx_buffer[FRAME_SIZE:] | ||
| else: | ||
| # Partial frame — keep it in buffer until next read | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in this case it might be worth actually waiting for the full packet to be received, it should take <0.1 ms, so would be minimal delay, and would mean that the packet is accounted for in that frame |
||
| break | ||
|
|
||
| latest_payload | ||
| return None # TODO: parse latest_payload into list of RobotResponse and return. | ||
|
|
||
| def send_robot_commands(self) -> None: | ||
| """Sends the robot commands to the appropriate team (yellow or blue).""" | ||
| # print(list(self.out_packet)) | ||
|
|
@@ -71,10 +105,6 @@ def send_robot_commands(self) -> None: | |
| f"Only {len(self._assigned_mapping)} out of {self._n_friendly} robots have been assigned commands. Sending incomplete command packet." | ||
| ) | ||
| self._serial_port.write(self._out_packet) | ||
| self._serial_port.read_all() | ||
| # data_in = self._serial.read_all() | ||
| # print(data_in) | ||
| # TODO: add receiving feedback from the robots | ||
|
|
||
| ### update kick and chip trackers. We persist the kick/chip command for KICKER_PERSIST_TIMESTEPS | ||
| ### this feature is to combat packet loss and to ensure the robot does not kick within its cooldown period | ||
|
|
@@ -145,18 +175,6 @@ def _add_robot_command(self, command: RobotCommand, robot_id: int) -> None: | |
| is_kick=False, | ||
| ) | ||
|
|
||
| # def _populate_robots_info(self, data_in: bytes) -> None: | ||
| # """ | ||
| # Populates the robots_info list with the data received from the robots. | ||
| # """ | ||
| # for i in range(self._n_friendly): | ||
| # has_ball = False | ||
| # if data_in[0] & 0b10000000: | ||
| # has_ball = True | ||
| # info = RobotInfo(has_ball=has_ball) | ||
| # self._robots_info[i] = info | ||
| # data_in = data_in << 1 # shift to the next robot's data | ||
|
|
||
| def _generate_command_buffer(self, robot_id: int, c_command: RobotCommand) -> bytes: | ||
| """Generates the command buffer to be sent to the robot.""" | ||
| assert robot_id < 6, "Invalid robot_id. Must be between 0 and 5." | ||
|
|
@@ -272,7 +290,7 @@ def _empty_command(self) -> bytearray: | |
| # * first byte is robot ID (here INVALID_RBT_ID) | ||
| # * remaining (self._rbt_cmd_size - 1) bytes are zeros | ||
| # - 0x55: end byte | ||
| cmd = bytearray([0xAA] + [INVALID_RBT_ID] + [0] * (self._rbt_cmd_size - 1) + [0x55]) | ||
| cmd = bytearray([self.HEADER] + [INVALID_RBT_ID] + [0] * (self._rbt_cmd_size - 1) + [self.TAIL]) | ||
| commands.extend(cmd) | ||
| self._cached_empty_command = commands | ||
| return self._cached_empty_command.copy() | ||
|
|
@@ -287,7 +305,7 @@ def _init_serial(self) -> Serial: | |
| bytesize=EIGHTBITS, # 8 data bits | ||
| parity=PARITY_EVEN, # Even parity (makes it 9 bits total) | ||
| stopbits=STOPBITS_TWO, # 2 stop bits | ||
| timeout=0.1, | ||
| timeout=0, | ||
| ) | ||
| return serial_port | ||
| except Exception as e: | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.