Conversation
Introduce UnitreeGo2- and TurtleBot4-specific odom/rplidar inputs and backgrounds and switch configs to use them. Many config JSON5 files updated to replace generic "Odom"/"RPLidar" types with UnitreeGo2 or TurtleBot4 variants (including renaming fabric_gps -> unitree_go2_fabric_gps and GPS reader to UnitreeGo2GPSOdomReader). Action connectors and background code imports were adjusted to use providers.unitree_go2_* and providers.turtlebot4_* implementations; RPLidar and odom background/input plugins were renamed or added (unitree_go2_rplidar, unitree_go2_odom, turtlebot4_odom, turtlebot4_rplidar). Several legacy test config files and the old generic odom background were removed. Tests and provider references were updated/renamed accordingly to align with the new provider/plugin names.
Refactor test patches to match renamed plugin/provider modules and classes with the unitree_go2_* prefix. Updated patch targets for IOProvider, OdomProvider/UnitreeGo2OdomProvider, RPLidarProvider/UnitreeGo2RPLidarProvider and related time/asyncio imports, adjusted assertion expectations (e.g. add_input source name and provider init args). These changes keep tests aligned with the refactored module and class names.
Rename mock and update references to match the UnitreeGo2RPLidar plugin. Renamed tests/integration/mock_inputs/mock_rplidar.py -> mock_unitree_go2_rplidar.py and class MockRPLidar -> MockUnitreeGo2RPLidar; updated tests/integration/mock_inputs/input_registry.py to import the new mock, register the mock under the "UnitreeGo2RPLidar" key, adjust mock module mappings, and update the unregister list; updated tests/integration/data/test_cases/rplidar_test.json5 to use type "UnitreeGo2RPLidar". This keeps mock names consistent with the real plugin identifier.
Remove Zenoh-related settings from the mock Unitree Go2 RPLidar input: drop the `use_zenoh` flag from the lidar config and remove the conditional handling that added `URID` and logging. This simplifies the test mock by eliminating Zenoh-specific configuration and behavior.
Remove the deprecated simple_paths option from RPLidar configs and providers (TurtleBot4 and Unitree Go2). Simplified path selection logic to always use the single default path and removed related config fields (use_zenoh, URID, machine_type where applicable). Update MoveZenoh connector to import and instantiate the TurtleBot4 RPLidar provider. Rename/cleanup the turtlebot4_odom background and add comprehensive unit tests for TurtleBot4 and Unitree odom and RPLidar backgrounds and inputs.
Add a comprehensive test suite for the TurtleBot4 RPLidar plugin. Tests cover RPLidarConfig defaults and custom values, TurtleBot4RPLidar initialization and provider parameter passing, async polling (_poll) with and without data, _raw_to_text and raw_to_text buffering behavior, formatted_latest_buffer (including buffer clearing and IOProvider interaction), and _extract_lidar_config. Uses unittest.mock and pytest (including async tests).
Add comprehensive unit tests for OdomProviderBase, TurtleBot4OdomProvider, and TurtleBot4RPLidarProvider. New test modules mock multiprocessing/threading and external dependencies (Zenoh, D435, sensor messages) and cover initialization, singleton behavior, start/stop, odometry processing (including quaternion->euler/yaw conversions), file logging, zenoh scan handling, path processing, and RPLidar config. Also remove an unused debug logging line from turtlebot4_rplidar_provider.py to tidy the implementation.
Fix incorrect tuple unpacking in tests/providers/test_turtlebot4_odom_provider.py so mock_process is assigned from the correct position in the mock_multiprocessing fixture. This ensures the test uses the intended mock process object when verifying that logging config is passed to the processor.
Clean up tests/providers/test_turtlebot4_rplidar_provider.py by removing redundant inline comments that described obvious assertions (subscriber declaration and path/angle checks). No functional changes—only test file comment cleanup to reduce noise.
Introduce a new UnitreeG1OdomProvider implementing OdomProviderBase to retrieve odometry/pose from Unitree G1 robots via CycloneDDS. Adds g1_odom_processor (runs in a separate process) which initializes the CycloneDDS channel, subscribes to rt/utlidar/robot_pose (PoseStamped_) and pushes messages into a multiprocessing queue; logging is configurable via existing logging helpers. The provider is a singleton that starts a multiprocessing subscriber and a local thread to process queue data, and includes _update_body_state to derive body height (cm) and RobotState (STANDING/SITTING). Optional imports are guarded with a warning if the Unitree SDK or CycloneDDS are not available. Requires unitree_sdk2py/CycloneDDS to function at runtime.
- Updated unitree_g1_autonomy.json5 to change the robot name from "Bits" to "Iris" and modified the system prompt accordingly. - Introduced UnitreeG1Odom background plugin for odometry data handling. - Added TurtleBot4Battery plugin for battery status monitoring with Zenoh integration. - Implemented UnitreeGo2Battery plugin for battery status with error handling for SDK absence. - Enhanced unit tests for TurtleBot4Battery and UnitreeGo2Battery plugins to ensure proper functionality and error handling.
There was a problem hiding this comment.
Pull request overview
Adds Booster (K1) Zenoh/ROS2 bridge support and associated tooling to send movement RPC commands and consume odometry via a new Odometer message, plus configs/scripts to exercise the integration.
Changes:
- Introduce Booster IDL message wrappers (
Odometer, RPC request/response,RemoteControllerState) and export them viazenoh_msgs. - Add a K1 odometry provider and Booster autonomy action connector that calls movement via Zenoh query/reply to a ROS2 service bridge.
- Add hardware-test scripts and new/updated configs for Booster autonomy + Zenoh diagnostics.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| system_hw_test/zenoh_echo.py | Utility to subscribe/pretty-print Zenoh topics with basic deserialization. |
| system_hw_test/test_booster_zenoh_odom_sub.py | Minimal subscriber script to verify odom topic traffic. |
| system_hw_test/test_booster_move_zenoh_service.py | Test client for sending movement RPC commands via Zenoh query/reply. |
| system_hw_test/test_booster_move.py | Test client for sending direct remote-controller-style movement messages. |
| system_hw_test/booster_zenoh_ros2_bridge.py | ROS2↔Zenoh bridge for odom/paths topics and RPC service query handling. |
| system_hw_test/booster_zenoh_mock.py | Mock Zenoh query responder for booster RPC service testing. |
| src/zenoh_msgs/idl/booster_interface.py | Adds Booster-specific IDL structs (odometer + RPC wrappers + controller state). |
| src/zenoh_msgs/idl/init.py | Re-exports Booster IDL types from zenoh_msgs.idl. |
| src/zenoh_msgs/init.py | Re-exports Booster IDL types at top-level zenoh_msgs. |
| src/providers/k1_odom_provider.py | New Zenoh-based odom provider that publishes OdomProviderBase.position. |
| src/inputs/plugins/unitree_go2_battery.py | Adjusts Go2 battery subscriber topic/type (currently broken). |
| src/inputs/plugins/booster_odom.py | New input plugin that surfaces Booster odom/movement status to the fuser. |
| src/actions/move_k1_autonomy/interface.py | Adds Booster movement action interface (LLM-facing enum + IO dataclasses). |
| src/actions/move_k1_autonomy/connector/k1_sdk.py | Implements movement connector using Zenoh RPC service + odom gating/safety logic. |
| config/greeting_local.json5 | Updates greeting local config (currently contains merge-conflict markers). |
| config/greeting.json5 | Changes greeting config globals (switches away from env interpolation). |
| config/booster_autonomy.json5 | New autonomy config for Booster using BoosterOdom + move_k1_autonomy. |
| <<<<<<< HEAD | ||
| api_key: "openmind_free", | ||
| unitree_ethernet: "", | ||
| ======= | ||
| api_key: "${OM_API_KEY:-openmind_free}", | ||
| unitree_ethernet: "${UNITREE_ETHERNET:-enP2p1s0}", | ||
| >>>>>>> origin/main |
There was a problem hiding this comment.
This file contains unresolved git merge-conflict markers (<<<<<<<, =======, >>>>>>>), which makes the JSON5 invalid and will break config loading. Resolve the conflict and keep a single intended value for api_key and unitree_ethernet.
| <<<<<<< HEAD | |
| api_key: "openmind_free", | |
| unitree_ethernet: "", | |
| ======= | |
| api_key: "${OM_API_KEY:-openmind_free}", | |
| unitree_ethernet: "${UNITREE_ETHERNET:-enP2p1s0}", | |
| >>>>>>> origin/main | |
| api_key: "${OM_API_KEY:-openmind_free}", | |
| unitree_ethernet: "${UNITREE_ETHERNET:-enP2p1s0}", |
| api_key: "openmind_free", | ||
| unitree_ethernet: "", |
There was a problem hiding this comment.
api_key and unitree_ethernet were changed from env-interpolated values to hard-coded strings. This breaks the established pattern used across other configs (e.g., config/conversation.json5) and makes it harder to run in different environments without editing tracked files; consider restoring ${OM_API_KEY:-...} / ${UNITREE_ETHERNET:-...} here (and keeping hard-coded defaults only in *_local configs if needed).
| api_key: "openmind_free", | |
| unitree_ethernet: "", | |
| api_key: "${OM_API_KEY:-openmind_free}", | |
| unitree_ethernet: "${UNITREE_ETHERNET:-}", |
| replies = self.session.get( | ||
| self.service_name, | ||
| payload=request_payload, | ||
| timeout=5.0, |
There was a problem hiding this comment.
The timeout parameter on _call_service() is ignored: session.get(..., timeout=5.0) uses a hard-coded value instead of the function argument. Use the timeout parameter (or remove it) so callers can control service-call timeouts consistently.
| timeout=5.0, | |
| timeout=timeout, |
|
|
||
| try: | ||
| self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) # type: ignore | ||
| self.lowstate_subscriber = ChannelSubscriber("rt/_BmsState_", BmsState_) # type: ignore |
There was a problem hiding this comment.
BmsState_ is referenced when creating the ChannelSubscriber, but it is never imported or defined (including in the ImportError fallback). This will raise a NameError at runtime; if the intent is to switch from LowState_ to a BMS-only message, update the imports/fallback stubs and ensure LowStateMessageHandler matches the subscribed message type.
| self.lowstate_subscriber = ChannelSubscriber("rt/_BmsState_", BmsState_) # type: ignore | |
| self.lowstate_subscriber = ChannelSubscriber("rt/_BmsState_", LowState_) # type: ignore |
❌ 2 Tests Failed:
View the top 2 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
| api_key: "${OM_API_KEY:-openmind_free}", | ||
| unitree_ethernet: "${UNITREE_ETHERNET:-enP2p1s0}", | ||
| api_key: "openmind_free", | ||
| unitree_ethernet: "", |
There was a problem hiding this comment.
Please restore these changes.
| <<<<<<< HEAD | ||
| api_key: "openmind_free", | ||
| unitree_ethernet: "", | ||
| ======= | ||
| api_key: "${OM_API_KEY:-openmind_free}", | ||
| unitree_ethernet: "${UNITREE_ETHERNET:-enP2p1s0}", | ||
| >>>>>>> origin/main |
There was a problem hiding this comment.
Please solve the conflicts.
| odom_topic : str | ||
| Zenoh topic for odometry data. | ||
| rpc_service_name : str | ||
| Zenoh key for the ROS2 RPC service (request/reply via session.get). | ||
| Defaults to "booster_rpc_service". |
| # Backward-compat: older configs used cmd_vel_topic for topic-based control. | ||
| # If provided, we treat it as the RPC service name. | ||
| cmd_vel_topic: Optional[str] = Field( | ||
| default=None, | ||
| description="DEPRECATED. Previously used for remote_controller_state topic; now interpreted as rpc_service_name.", | ||
| ) | ||
|
|
||
| allow_move_without_odom: bool = Field( | ||
| default=False, | ||
| description="TESTING ONLY. If true, bypass odom/body-attitude gating and send movement RPC commands even when odom is missing.", | ||
| ) |
There was a problem hiding this comment.
Please remove it if you don't need it
| import json | ||
|
|
||
| from zenoh_msgs import ( | ||
| BoosterApiReqMsg, | ||
| RpcServiceRequest, | ||
| RpcServiceResponse, | ||
| ) | ||
|
|
There was a problem hiding this comment.
Please put all import to the top.
|
|
||
| try: | ||
| self.lowstate_subscriber = ChannelSubscriber("rt/lowstate", LowState_) # type: ignore | ||
| self.lowstate_subscriber = ChannelSubscriber("rt/_BmsState_", BmsState_) # type: ignore |
| Parameters | ||
| ---------- | ||
| topic : str | ||
| The Zenoh topic to subscribe to for odometry data. | ||
| Defaults to "odometer_state". |
| """Booster Interface Messages.""" | ||
|
|
Overview
[Provide a brief overview of the changes in this pull request.]
(If applicable, linked issue: [ ])
Type of change
Changes
[Detail the changes you have made in this pull request. Include any new features, bug fixes, or improvements.]
Checklist
Impact
[Explain the impact of your changes. Include any potential risks or side effects that reviewers should be aware of.]
Additional Information
[Include any additional information that may be relevant to reviewers. This could include links to related issues or pull requests, references to documentation, or other context that may help reviewers understand your changes.]