8000 feat: unified ROS2 hri message by maciejmajek · Pull Request #416 · RobotecAI/rai · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: unified ROS2 hri message #416

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

Merged
merged 8 commits into from
Feb 11, 2025

Conversation

maciejmajek
Copy link
Member
@maciejmajek maciejmajek commented Feb 10, 2025

Purpose

HRIMessage is used for human-robot interaction. In the current implementation, a simple String message was used instead which is very limiting.

Proposed Changes

  • Adds a custom ROS2 HRIMessage message
  • Simplified ROS2HRIConnector initialization (removed the need to specify TopicConfig)

Testing

  • CI + new tests

Summary by CodeRabbit

  • New Features
    • Enhanced human–robot interaction communications with new multimedia message formats that combine text, images, and audio.
    • Introduced a default message type for topic configurations, streamlining setup and reducing the need for manual specification.
    • Improved messaging connectors for flexible configuration and robust conversion between internal and external message representations.

@maciejmajek
Copy link
Member Author

@coderabbitai full review

Copy link
Contributor
coderabbitai bot commented Feb 10, 2025

Walkthrough

This pull request introduces several modifications to enhance the ROS2 communication framework. It changes the default message type in the TopicConfig class, augments the ROS2HRIMessage and ROS2HRIConnector classes with new message conversion methods and flexible configuration, adds new ROS2 message definitions for audio and HRI interactions, and expands the test suite with new publisher/subscriber classes and tests to validate HRI message handling.

Changes

Files Change Summaries
src/rai_core/rai/communication/ros2/api.py Updated TopicConfig: changed msg_type from a required parameter to a default value ("rai_interfaces/msg/HRIMessage").
src/rai_core/rai/communication/ros2/connectors.py Enhanced ROS2HRIMessage with from_ros2 and to_ros2_dict methods; updated ROS2HRIConnector constructor to support mixed types for targets and sources; refactored send_message and receive_message to use new methods.
src/rai_interfaces/CMakeLists.txt, src/rai_interfaces/msg/AudioMessage.msg, src/rai_interfaces/msg/HRIMessage.msg Added new message definitions: AudioMessage (for audio data) and HRIMessage (for multi-modal human-robot interaction data) to the interface generation list and message definitions.
tests/communication/ros2/helpers.py Introduced new classes HRIMessagePublisher and HRIMessageSubscriber for handling the publishing and subscription of HRIMessage instances in tests.
tests/communication/ros2/test_api.py, tests/communication/ros2/test_connectors.py Modified instantiation of TopicConfig to use keyword arguments for msg_type and added tests validating the new HRI message publish/subscribe flow.

Sequence Diagram(s)

sequenceDiagram
    participant Pub as HRIMessagePublisher
    participant Conn as ROS2HRIConnector
    participant Msg as ROS2HRIMessage
    participant Sub as HRIMessageSubscriber

    Pub->>Conn: Publish HRI message payload
    Conn->>Msg: Convert message using to_ros2_dict()
    Msg-->>Conn: Return ROS2 message dict
    Conn->>Sub: Transmit message over ROS2 network
    Sub->>Msg: Process message using from_ros2()
    Msg-->>Sub: Return structured HRI message
Loading

Possibly related PRs

Suggested reviewers

  • boczekbartek
✨ Finishing Touches
  • 📝 Docstrings were successfully generated. (♻️ Check again to generate again)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (6)
src/rai_interfaces/msg/AudioMessage.msg (1)

1-19: Confirm channel type.
Using int16 for channels is unusual, since the channel count is generally non-negative. If only positive values are expected, consider using uint16. Otherwise, this is fine as-is.

tests/communication/ros2/helpers.py (2)

38-52: Consider adding error handling for image conversion.

The publish_message method assumes image conversion will always succeed. Consider adding try-catch blocks for potential CvBridge errors.

 def publish_message(self) -> None:
     msg = HRIMessage()
-    image = self.cv_bridge.cv2_to_imgmsg(np.zeros((100, 100, 3), dtype=np.uint8))
-    msg.images = [image]
+    try:
+        image = self.cv_bridge.cv2_to_imgmsg(np.zeros((100, 100, 3), dtype=np.uint8))
+        msg.images = [image]
+    except Exception as e:
+        self.get_logger().error(f"Failed to convert image: {e}")
+        return
     msg.audios = [AudioSegment.silent(duration=1000)]
     msg.text = "Hello, HRI!"
     self.publisher.publish(msg)

54-64: Add cleanup for subscription in HRIMessageSubscriber.

The class should implement proper cleanup to prevent resource leaks.

 class HRIMessageSubscriber(Node):
     def __init__(self, topic: str):
         super().__init__("test_hri_message_subscriber")
         self.subscription = self.create_subscription(
             HRIMessage, topic, self.handle_test_message, 10
         )
         self.received_messages: List[HRIMessage] = []

     def handle_test_message(self, msg: HRIMessage) -> None:
         self.received_messages.append(msg)
+
+    def destroy(self) -> None:
+        """Cleanup subscription when the node is destroyed."""
+        self.destroy_subscription(self.subscription)
+        super().destroy_node()
tests/communication/ros2/test_connectors.py (2)

176-214: Consider adding more test cases and improving assertions.

While the test covers basic functionality, consider:

  1. Testing edge cases (empty images/audio)
  2. Testing invalid message author
  3. Using more descriptive assertion messages
 def test_ros2hri_default_message_publish(
     ros_setup: None, request: pytest.FixtureRequest
 ):
     topic_name = f"{request.node.originalname}_topic"  # type: ignore
     connector = ROS2HRIConnector(targets=[topic_name])
     hri_message_receiver = HRIMessageSubscriber(topic_name)
     executors, threads = multi_threaded_spinner([hri_message_receiver])

     try:
         images = [Image.new("RGB", (100, 100), color="red")]
         audios = [AudioSegment.silent(duration=1000)]
         text = "Hello, HRI!"
         payload = HRIPayload(images=images, audios=audios, text=text)
         message = ROS2HRIMessage(payload=payload, message_author="ai")
         connector.send_message(message, target=topic_name)
         time.sleep(1)  # wait for the message to be received

-        assert len(hri_message_receiver.received_messages) > 0
+        assert len(hri_message_receiver.received_messages) > 0, "No messages received"
         recreated_message = ROS2HRIMessage.from_ros2(
             hri_message_receiver.received_messages[0], message_author="ai"
         )

-        assert message.text == recreated_message.text
-        assert message.message_author == recreated_message.message_author
-        assert len(message.images) == len(recreated_message.images)
-        assert len(message.audios) == len(recreated_message.audios)
+        assert message.text == recreated_message.text, "Text mismatch"
+        assert message.message_author == recreated_message.message_author, "Author mismatch"
+        assert len(message.images) == len(recreated_message.images), "Image count mismatch"
+        assert len(message.audios) == len(recreated_message.audios), "Audio count mismatch"

191-191: Replace fixed sleep with dynamic waiting.

Instead of using a fixed time.sleep(1), consider implementing a wait_for_messages helper that polls with a timeout.

-        time.sleep(1)  # wait for the message to be received
+        def wait_for_messages(timeout: float = 1.0) -> bool:
+            start_time = time.time()
+            while time.time() - start_time < timeout:
+                if len(hri_message_receiver.received_messages) > 0:
+                    return True
+                time.sleep(0.1)
+            return False
+
+        assert wait_for_messages(), f"No messages received within {timeout} seconds"
src/rai_interfaces/CMakeLists.txt (1)

24-25: Approved: New Message Definitions Added

The addition of "msg/AudioMessage.msg" and "msg/HRIMessage.msg" to the rosidl_generate_interfaces call successfully expands the project’s ROS2 interface definitions in a way that aligns with the PR objectives. Please ensure that the corresponding message files exist in the src/rai_interfaces/msg/ directory and that they conform to the correct ROS2 message syntax. It would also be useful to verify that any downstream dependencies (e.g., in the connector or topic configuration) have been appropriately updated for these new message types.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce4885c and 297832d.

📒 Files selected for processing (8)
  • src/rai_core/rai/communication/ros2/api.py (1 hunks)
  • src/rai_core/rai/communication/ros2/connectors.py (6 hunks)
  • src/rai_interfaces/CMakeLists.txt (1 hunks)
  • src/rai_interfaces/msg/AudioMessage.msg (1 hunks)
  • src/rai_interfaces/msg/HRIMessage.msg (1 hunks)
  • tests/communication/ros2/helpers.py (2 hunks)
  • tests/communication/ros2/test_api.py (3 hunks)
  • tests/communication/ros2/test_connectors.py (2 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
src/rai_core/rai/communication/ros2/connectors.py

273-273: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)


274-274: Do not use mutable data structures for argument defaults

Replace with None; initialize within function

(B006)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test-ros2 (humble)
🔇 Additional comments (8)
src/rai_core/rai/communication/ros2/connectors.py (5)

18-20: Imports look good.
No immediate concerns with the new dependencies. The addition of NumPy, PIL, and pydub appears consistent with the new image/audio handling functionality.

Also applies to: 25-27, 32-32, 49-52


213-240: Verify audio sample width.
Currently, sample_width=2 is hard-coded as 16-bit audio (int16). Make sure this is always correct for all incoming AudioMessages. If not, consider dynamically determining the sample width or enforcing a specific audio format in the interface.


319-322: Sending updated HRI messages looks correct.
Using message.to_ros2_dict() for publishing ensures consistency with the new ROS2 message structure.


340-340: Receive message logic is clean.
Converting the raw ROS2 message into ROS2HRIMessage via from_ros2 is consistent with the new interface.


367-372: Shutdown sequence looks good.
Graceful termination of the executor, thread, and APIs is correct and ensures clean resource release.

src/rai_interfaces/msg/HRIMessage.msg (1)

1-20: Overall structure looks good.
This message fields layout comprehensively covers text, images, and audio for HRI use cases.

tests/communication/ros2/test_api.py (1)

74-74: LGTM! Clean transition to keyword arguments.

The changes consistently update TopicConfig instantiation to use keyword arguments, improving code clarity.

Also applies to: 89-90, 111-112

tests/communication/ros2/test_connectors.py (1)

190-190: Consider validating message_author.

Add validation for the message_author parameter to ensure it's not empty or invalid.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Check if message_author validation exists
rg "message_author.*validation" --type py

Length of output: 43


Action Required: Add Validation for the message_author Parameter
Our search did not reveal any validation logic for message_author in the codebase. In particular, the command to look for patterns like "message_author.*validation" produced no output. This confirms that the current implementation lacks a check to ensure that the message_author parameter is neither empty nor invalid before sending messages. Please consider adding the necessary validation logic to the relevant functions (e.g., within the connector’s send_message implementation) to avoid potenti 8000 al issues at runtime.

@maciejmajek maciejmajek requested a review from rachwalk February 11, 2025 08:57
coderabbitai bot added a commit that referenced this pull request Feb 11, 2025
Docstrings generation was requested by @rachwalk.

* #416 (comment)

The following files were modified:

* `src/rai_core/rai/communication/ros2/connectors.py`
* `tests/communication/ros2/helpers.py`
* `tests/communication/ros2/test_api.py`
* `tests/communication/ros2/test_connectors.py`
Copy link
Contributor
coderabbitai bot commented Feb 11, 2025

Note

Generated docstrings for this pull request at #417

Copy link
Contributor
@rachwalk rachwalk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding the much needed HRIMessages to rai_interfaces, few changes regarding type hints. I made docstrings using code rabbit https://github.com/RobotecAI/rai/pull/417/files please review them, and change them where applicable regarding the requested changes. I think also that coderabbit might be generating the docs overeagerly with docstrings

Comment on lines 214 to 221
@classmethod
def from_ros2(cls, msg: Any, message_author: Literal["ai", "human"]):
from rai_interfaces.msg import HRIMessage as ROS2HRIMessage_

if not isinstance(msg, ROS2HRIMessage_):
raise ValueError(
"ROS2HRIMessage can only be created from rai_interfaces/msg/HRIMessage"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not a top level import? If it can be it should be, and the type annotations should utilise it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8e26366
I think it's ok now

Comment on lines 243 to 244
def to_ros2_dict(self):
cv_bridge = CvBridge()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add return type annotation, since it's enforeced to be OrderedDict (alternatively consider a cast to TypedDict, since in this case it's guaranteed to succeed, and will provide additional information to API user).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message definition is considered "floating" (python code is built from .msg file during colcon build), therefore it would be hard to cast it to TypedDict. I will update the return type

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maciejmajek maciejmajek requested a review from rachwalk February 11, 2025 16:12
Copy link
Contributor
@rachwalk rachwalk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but following private disscussion please add an issue to add the docstrings for these changes and reference it in the PR merge comment.

@maciejmajek maciejmajek merged commit 197f61e into development Feb 11, 2025
5 checks passed
@maciejmajek maciejmajek deleted the feat/unified-hri-ros2-message branch February 11, 2025 16:14
jmatejcz pushed a commit that referenced this pull request Feb 17, 2025
Further work: #418

refactor(partial): refactor openset tools to rai2.0 ros2ariconnector api

refactor: finalize with simplifications

fix: missing argument, missing client

workaround: don't remove subscription due to rclpy issue

ros2/rclpy#1142

replace ros2 future waiting with more robust callback based mechanism

remove waiting from launch

fix order in connector shutdown

```
Exception in thread Thread-1 (spin):
Traceback (most recent call last):
  File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.10/threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 294, in spin
    self.spin_once()
  File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 794, in spin_once
    self._spin_once_impl(timeout_sec)
  File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 786, in _spin_once_impl
    self._executor.submit(handler)
  File "/usr/lib/python3.10/concurrent/futures/thread.py", line 167, in submit
    raise RuntimeError('cannot schedule new futures after shutdown')
RuntimeError: cannot schedule new futures after shutdown
```

refactor: cleanup manipulation launchfile

chore: pre-commit

feat: benchmark class models

Co-authored-by: Jakub Matejczyk <jakub.matejczyk@robotec.ai>

chore: naming changes

Co-authored-by: Jakub Matejczyk <jakub.matejczyk@robotec.ai>

chore: naming changes

Co-authored-by: Jakub Matejczyk <jakub.matejczyk@robotec.ai>

chore: redefine benchmark model

Co-authored-by: Jakub Matejczyk <jakub.matejczyk@robotec.ai>
Co-authored-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

refactor: SceneConfig to BaseModel class

feat: add SceneSetup to store initial scene setup

build: poetry initialization of rai_benchmarks and rai_simulations

chore: add licence lines

build: create packages from rai_benchmarks and rai_simulations

chore: removed mistakenly added file

feat: scene config implementation

Add O3DEEngineConnector features

Signed-off-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

Remove an unused file

Signed-off-by: Kacper
6D47
 Dąbrowski <kacper.dabrowski@robotec.ai>

Add binary path caching

Signed-off-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

Add two example scenes

Signed-off-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

feat: replace binary path with ros2 launch command + binary path

refactor: renamed rai_sim and rai_bench

fix: fixed shutdown of binary

chore: make pose mandatory

chore: remove rai_bench because it is developed on another branch

ci: add missing license lines

laoding and spawning frm benchmark

rabsed

naming change

grab xyz benchmark

benchmarks run new sim every scenario for now

adjust to new tools

Remove psutil conflicting __del__ method from o3de connector

See this issue: giampaolo/psutil#2437

stream langgraph agent

extent output of tool runner

enable all scenarios in the benchmark

removed name providing for manipulator launch

added move arm func to connectors
jmatejcz pushed a commit that referenced this pull request Feb 18, 2025
Further work: #418

refactor(partial): refactor openset tools to rai2.0 ros2ariconnector api

refactor: finalize with simplifications

fix: missing argument, missing client

workaround: don't remove subscription due to rclpy issue

ros2/rclpy#1142

replace ros2 future waiting with more robust callback based mechanism

remove waiting from launch

fix order in connector shutdown

```
Exception in thread Thread-1 (spin):
Traceback (most recent call last):
  File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.10/threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 294, in spin
    self.spin_once()
  File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 794, in spin_once
    self._spin_once_impl(timeout_sec)
  File "/opt/ros/humble/local/lib/python3.10/dist-packages/rclpy/executors.py", line 786, in _spin_once_impl
    self._executor.submit(handler)
  File "/usr/lib/python3.10/concurrent/futures/thread.py", line 167, in submit
    raise RuntimeError('cannot schedule new futures after shutdown')
RuntimeError: cannot schedule new futures after shutdown
```

refactor: cleanup manipulation launchfile

chore: pre-commit

feat: benchmark class models

Co-authored-by: Jakub Matejczyk <jakub.matejczyk@robotec.ai>

chore: naming changes

Co-authored-by: Jakub Matejczyk <jakub.matejczyk@robotec.ai>

chore: naming changes

Co-authored-by: Jakub Matejczyk <jakub.matejczyk@robotec.ai>

chore: redefine benchmark model

Co-authored-by: Jakub Matejczyk <jakub.matejczyk@robotec.ai>
Co-authored-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

refactor: SceneConfig to BaseModel class

feat: add SceneSetup to store initial scene setup

build: poetry initialization of rai_benchmarks and rai_simulations

chore: add licence lines

build: create packages from rai_benchmarks and rai_simulations

chore: removed mistakenly added file

feat: scene config implementation

Add O3DEEngineConnector features

Signed-off-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

Remove an unused file

Signed-off-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

Add binary path caching

Signed-off-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

Add two example scenes

Signed-off-by: Kacper Dąbrowski <kacper.dabrowski@robotec.ai>

feat: replace binary path with ros2 launch command + binary path

refactor: renamed rai_sim and rai_bench

fix: fixed shutdown of binary

chore: make pose mandatory

chore: remove rai_bench because it is developed on another branch

ci: add missing license lines

laoding and spawning frm benchmark

rabsed

naming change

grab xyz benchmark

benchmarks run new sim every scenario for now

adjust to new tools

Remove psutil conflicting __del__ method from o3de connector

See this issue: giampaolo/psutil#2437

stream langgraph agent

extent output of tool runner

enable all scenarios in the benchmark

removed name providing for manipulator launch

added move arm func to connectors

rebased and adjusted to new code

added metrics to tasks, created rai_bench package

added comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0