Skip to content

[BTB-455] feat. dimos to battlebang compatible#3

Open
tosemfdk wants to merge 2 commits intodevelopfrom
feature/BTB-445-simul-compatible
Open

[BTB-455] feat. dimos to battlebang compatible#3
tosemfdk wants to merge 2 commits intodevelopfrom
feature/BTB-445-simul-compatible

Conversation

@tosemfdk
Copy link
Contributor

@tosemfdk tosemfdk commented Mar 3, 2026

simulation상에서 dimos와 battlebang을 연결하였습니다.

  • battlebang에서 필요한 lidar, tf, yolo bbox등을 ros2 topic으로 발행하여 사람을 발견하였을때 combat, searching등의 action이 정상적으로 실행되도록 하였습니다.
  • dimos 시스템 내부의 move명령을 내리는 /cmd_vel을 ros2의 /cmd_vel_dimos로 변경하여 battlebang의 twist mux를 거쳐야만 move명령이 작동하도록 하였습니다. 마찬가지로 twist mux를 거친 ros2의 /cmd_vel_out 토픽은 기존에 dimos에 존재하던 /cmd_vel 채널로 입력되게 됩니다.
  • hal node에서 들어오는 /webrtc_req 토픽을 dimos로 매핑하여 api명령이 전달되도록 하였습니다. (실제 go2에서 테스트 해보진 못했습니다.)
    Uploading 스크린캐스트 03-03-2026 06:10:46 PM.webm…

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • Unitree GO2 Battlebang 브릿지 모듈 추가
    • 어노테이션 발행 구성 옵션 추가
  • 버그 수정

    • ROS 메시지 호환성 및 유형 강제 변환 개선
    • 웹소켓 모듈 종료 시퀀스 안정성 향상
  • 테스트

    • Detection2D 라운드트립 검증 테스트 추가
  • 기타

    • TFMessage 지원 추가
    • 사람 감지 유효성 검사 기준 조정

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

📝 Walkthrough

변경 사항 분석

Walkthrough

이 PR은 ROS 감지 메시지의 class_id 인코딩을 개선하고, 새로운 Battlebang WebRTC 브리지를 추가하며, ROS 타입 강제 변환 로직을 강화하고, 웹소켓 종료를 안전하게 처리합니다.

Changes

Cohort / File(s) Summary
Detection class_id 인코딩/디코딩
dimos/perception/detection/type/detection2d/bbox.py, dimos/perception/detection/type/detection2d/point.py, dimos/perception/detection/test_bbox_detectors.py, dimos/perception/detection/type/detection2d/test_imageDetections2D.py
Detection2DBBox에 class_id를 "id:name" 형식으로 인코딩/디코딩하는 정적 헬퍼 메서드 추가. ROS ObjectHypothesis 변환 시 이를 사용하고, from_ros 재구성에서 역디코딩 처리. Point 타입도 class_id를 문자열로 변환. 라운드-트립 검증 테스트 추가.
Detection 검증 및 구성 변경
dimos/perception/detection/type/detection2d/person.py, dimos/perception/detection/module2D.py
Person 유효성 검사: keypoint_score 임계값을 0.8에서 0.5로 낮추고 필요한 최소 유효 키포인트를 5개에서 1개로 감소. Module2D에 publish_annotations 구성 옵션(기본값 true) 추가하고 조건부 구독 로직 적용.
ROS 타입 강제 변환 개선
dimos/protocol/pubsub/impl/rospubsub_conversion.py
_coerce_lcm_scalar_to_ros_type 헬퍼 함수 추가하여 LCM 스칼라 값을 ROS 필드 타입과 일치시킴. COMPLEX_TYPES에 TFMessage 추가. _copy_lcm_to_ros_recursive에서 원시 필드 처리 시 강제 변환 적용.
Battlebang WebRTC 브리지 구현
dimos/robot/unitree/go2/battlebang_webrtc_bridge.py, dimos/robot/unitree/go2/blueprints/smart/unitree_go2_battlebang_bridge.py, dimos/robot/unitree/go2/blueprints/__init__.py
새로운 BattlebangWebrtcBridge 모듈로 ROS 토픽을 GO2 RPC 엔드포인트에 연결. WebRTC 요청 및 cmd_vel 브리징 지원. YoloPersonDetector 및 재맵핑 규칙을 포함한 통합 blueprint 제공.
GO2 연결 및 시뮬레이션 업데이트
dimos/robot/unitree/go2/connection.py, dimos/robot/unitree/go2/ros_sim_connection.py, dimos/robot/all_blueprints.py
GO2Connection에 tf_msg 출력 채널 추가하여 TFMessage 발행 지원. ROSSimConnection의 기본 cmd_vel 토픽을 "cmd_vel_out"에서 "cmd_vel"로 변경. all_blueprints에 battlebang 브리지 등록.
웹소켓 종료 안전성 강화
dimos/web/websocket_vis/websocket_vis_module.py
stop() 메서드에서 Socket.IO 서버 안전 종료 시퀀스 추가: sio 참조 지역화, 명시적 shutdown() 호출 시도, 타임아웃 및 예외 로깅. _emit()에 sio 유효성 검사 및 try/except 래핑 추가.

Sequence Diagram(s)

sequenceDiagram
    participant App as Detection2D 앱
    participant Det as Detection2DBBox
    participant ROS as ROS ObjectHypothesis
    participant Conv as 변환 로직
    
    App->>Det: Detection2DBBox 생성<br/>(class_id=5, name="person")
    Det->>Det: _encode_ros_class_id(5, "person")<br/>→ "5:person"
    Det->>ROS: to_ros_detection2d() 호출
    ROS->>ROS: ObjectHypothesis.class_id = "5:person"
    ROS-->>App: ROS Detection2D 반환
    
    App->>Conv: from_ros_detection2d() 호출
    Conv->>Det: _decode_ros_class_id("5:person")
    Det->>Det: 파싱: class_id=5, name="person"
    Conv->>Det: Detection2DBBox 재구성
    Det-->>App: 원본과 동일한<br/>Detection2DBBox 반환
Loading
sequenceDiagram
    participant ROS as ROS 토픽
    participant Bridge as BattlebangWebrtcBridge
    participant RPC as GO2Connection RPC
    participant GO2 as GO2 유닛
    
    ROS->>Bridge: webrtc_req 메시지 도착
    Bridge->>Bridge: _parse_parameter()로<br/>페이로드 파싱
    Bridge->>Bridge: topic/api_id 검증
    Bridge->>RPC: publish_request(payload)
    RPC->>GO2: WebRTC 요청 전송
    GO2-->>RPC: 응답
    RPC-->>Bridge: 완료
    
    ROS->>Bridge: cmd_vel Twist 도착
    Bridge->>Bridge: ROS Twist→<br/>DimOS 표현 변환
    Bridge->>RPC: move(velocity)
    RPC->>GO2: 이동 명령 실행
    GO2-->>RPC: 확인
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 클래스 ID를 인코딩하고
WebRTC 브릿지로 연결하며
GO2 로봇은 춤을 추네!
타입 강제 변환으로 안전하게,
웹소켓은 우아하게 종료된다

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning 제공된 설명은 저장소의 필수 템플릿 구조(Problem, Solution, Breaking Changes, How to Test)를 따르지 않고 있습니다. 제공된 설명 템플릿에 따라 Problem, Solution, Breaking Changes, How to Test 섹션을 포함하도록 PR 설명을 작성해주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 PR의 주요 변경 사항인 battlebang과의 호환성 추가를 명확하게 설명하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/BTB-445-simul-compatible

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 Dimos 시스템과 Battlebang 간의 시뮬레이션 통합을 목표로 합니다. 주요 변경 사항은 Battlebang이 사람을 감지하고 행동을 실행할 수 있도록 ROS2 토픽을 통해 센서 데이터를 발행하는 기능, Dimos의 이동 명령을 Battlebang의 twist mux를 통해 라우팅하는 cmd_vel 토픽 리매핑, 그리고 WebRTC 요청을 Dimos API 명령으로 변환하는 브릿지 추가입니다. 또한, ROS 메시지 타입 변환의 견고성을 높이고 사람 감지 로직을 조정하여 전반적인 시스템 상호 운용성을 향상시켰습니다.

Highlights

  • Dimos-Battlebang 통합: 시뮬레이션 환경에서 Dimos 시스템과 Battlebang을 연결하여 사람 감지 및 전투/수색 액션이 정상적으로 실행되도록 하였습니다.
  • ROS2 토픽 발행: Battlebang에서 필요한 lidar, tf, yolo bbox 등의 데이터를 ROS2 토픽으로 발행하도록 구현했습니다.
  • cmd_vel 토픽 리매핑: Dimos 내부의 /cmd_vel 명령을 /cmd_vel_dimos로 변경하여 Battlebang의 twist mux를 거치도록 했으며, twist mux의 출력인 /cmd_vel_out은 다시 Dimos의 /cmd_vel로 입력됩니다.
  • WebRTC 요청 매핑: hal 노드에서 들어오는 /webrtc_req 토픽을 Dimos로 매핑하여 API 명령이 전달되도록 처리했습니다.
  • 새로운 블루프린트 추가: unitree-go2-battlebang-bridge 블루프린트와 battlebang_webrtc_bridge 모듈을 추가하여 통합 기능을 제공합니다.
  • ROS 타입 강제 변환 개선: ROS 런타임 필드 어설션을 위한 스칼라 값 강제 변환 로직을 추가하여, LCM 메시지의 숫자 값이 ROS의 문자열 필드에 할당될 때 발생하는 타입 불일치 문제를 해결했습니다.
  • 사람 감지 유효성 로직 완화: 사람 감지 시 유효 키포인트 점수 임계값을 0.8에서 0.5로, 최소 유효 키포인트 개수를 5개에서 1개로 완화하여 감지 유효성 기준을 조정했습니다.
  • TFMessage 발행 기능 추가: GO2Connectiontf_msg 출력 포트를 추가하고, TF 메시지를 ROS로 발행하는 기능을 구현했습니다.
  • WebSocket 모듈 종료 처리 개선: WebSocket 시각화 모듈의 stop 메서드에서 Socket.IO 종료 로직을 개선하여, 종료 중 새로운 emit 스케줄링을 방지하고 안정적인 종료를 보장합니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • dimos/perception/detection/module2D.py
    • 새로운 설정 옵션인 publish_annotations를 추가했습니다.
    • publish_annotations 설정에 따라 주석 발행 구독을 조건부로 처리하도록 수정했습니다.
  • dimos/perception/detection/type/detection2d/bbox.py
    • ROS2 vision_msgs에서 문자열 class_id를 정수로 변환하여 처리하도록 from_ros_detection2d 메서드를 수정했습니다.
    • to_ros_detection2d 메서드에서 class_id를 ROS2 vision_msgs에 맞게 문자열로 변환하도록 수정했습니다.
  • dimos/perception/detection/type/detection2d/person.py
    • 사람 감지 유효성 검사에서 키포인트 점수 임계값을 0.8에서 0.5로 낮췄습니다.
    • 사람 감지 유효성 검사에서 필요한 최소 유효 키포인트 개수를 5개에서 1개로 줄였습니다.
  • dimos/perception/detection/type/detection2d/point.py
    • to_ros_detection2d 메서드에서 class_id를 ROS2 vision_msgs에 맞게 문자열로 변환하도록 수정했습니다.
  • dimos/protocol/pubsub/impl/rospubsub_conversion.py
    • 알려진 ROS 메시지 타입 목록에 tf2_msgs.TFMessage를 추가했습니다.
    • ROS 런타임 필드 어설션을 위한 스칼라 값 강제 변환 함수 _coerce_lcm_scalar_to_ros_type를 도입했습니다.
    • _copy_lcm_to_ros_recursive 함수에서 새로운 강제 변환 함수를 적용했습니다.
  • dimos/robot/all_blueprints.py
    • all_blueprintsunitree-go2-battlebang-bridge를 추가했습니다.
    • all_modulesbattlebang_webrtc_bridge를 추가했습니다.
  • dimos/robot/unitree/go2/battlebang_webrtc_bridge.py
    • /webrtc_reqGO2Connection.publish_request로, 선택적으로 /cmd_vel_outGO2Connection.move로 브릿지하는 BattlebangWebrtcBridge 새 모듈을 추가했습니다.
  • dimos/robot/unitree/go2/blueprints/init.py
    • 스마트 블루프린트 목록에 unitree_go2_battlebang_bridge를 추가했습니다.
  • dimos/robot/unitree/go2/blueprints/smart/unitree_go2_battlebang_bridge.py
    • unitree_go2, battlebang_webrtc_bridge, Detection2DModule을 통합하는 unitree_go2_battlebang_bridge 새 블루프린트를 추가했습니다.
    • twist mux 통합을 위해 cmd_velcmd_vel_dimoscmd_vel_out으로 라우팅하는 리매핑을 구성했습니다.
    • cmd_vel_dimos, cmd_vel_out, detections, lidar, camera_info, tf_msg에 대한 ROS 트랜스포트를 정의했습니다.
  • dimos/robot/unitree/go2/connection.py
    • TFMessage를 임포트했습니다.
    • GO2Connectiontf_msg를 출력 포트로 추가했습니다.
    • tf_msg.transport가 사용 가능한 경우 TFMessage를 발행하도록 _publish_tf를 수정했습니다.
  • dimos/robot/unitree/go2/ros_sim_connection.py
    • 기본 cmd_vel_topiccmd_vel_out에서 cmd_vel로 변경했습니다.
  • dimos/web/websocket_vis/websocket_vis_module.py
    • Socket.IO 종료 중 새로운 emit 스케줄링을 방지하기 위해 stop 메서드에서 Socket.IO 종료 처리를 개선했습니다.
    • 종료 중 sio가 None일 경우 발생할 수 있는 오류를 방지하기 위해 emit 전에 self.sio 존재 여부를 확인하는 로직을 추가했습니다.
Activity
  • PR 작성자 tosemfdk가 Dimos와 Battlebang의 시뮬레이션 통합에 대한 변경 사항을 설명했습니다.
  • ROS2 토픽을 통해 lidar, tf, yolo bbox와 같은 센서 데이터를 발행하여 Battlebang이 사람을 감지하고 전투/수색 액션을 수행할 수 있도록 하는 내용이 포함되었습니다.
  • Dimos의 /cmd_vel 명령이 Battlebang의 twist mux를 거쳐 라우팅되도록 변경되었으며, twist mux의 출력은 다시 Dimos의 /cmd_vel로 입력되는 과정이 설명되었습니다.
  • hal 노드에서 오는 /webrtc_req 토픽을 Dimos로 매핑하여 API 명령을 전달하는 기능이 추가되었으나, 실제 Go2 로봇에서는 테스트되지 않았다고 언급되었습니다.
  • 시뮬레이션 데모를 보여주는 스크린캐스트가 업로드되었습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This PR integrates the dimos and battlebang systems within a simulation environment, adapting dimos's object detection, TF, and movement commands for battlebang compatibility using ROS2 topics, and controlling movement via battlebang's twist_mux. It also bridges the webrtc_req topic to transmit API commands. A critical security concern has been identified: the newly introduced bridge between ROS topics and the robot's WebRTC API lacks access control, potentially allowing arbitrary API calls to the robot from the ROS network. Additionally, improvements are suggested for exception handling in the BattlebangWebrtcBridge module and the validation logic of Detection2DPerson.

logger.warning("[BattlebangWebrtcBridge] dropping webrtc request: empty topic")
return

api_id = int(getattr(msg, "api_id", -1))

Choose a reason for hiding this comment

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

high

getattr(msg, "api_id", -1)의 결과를 int()로 변환할 때 ValueError가 발생할 수 있습니다. 예를 들어, api_id 필드가 숫자로 변환할 수 없는 문자열(예: "abc")일 경우 프로그램이 비정상적으로 종료될 수 있습니다. try-except 블록을 사용하여 ValueError를 처리하여 안정성을 높이는 것이 좋습니다.

Suggested change
api_id = int(getattr(msg, "api_id", -1))
api_id_raw = getattr(msg, "api_id", -1)
try:
api_id = int(api_id_raw)
except (ValueError, TypeError):
logger.warning(
f"[BattlebangWebrtcBridge] dropping webrtc request: invalid api_id format '{api_id_raw}'"
)
return

payload["parameter"] = parameter

try:
self._publish_request_rpc(topic, payload)

Choose a reason for hiding this comment

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

security-medium medium

The _on_webrtc_req_raw method forwards ROS messages from the /webrtc_req topic to the robot's WebRTC API without any validation or authorization. This allows any user or process on the ROS network to call arbitrary API methods on the robot, potentially leading to unauthorized control or configuration changes. It is recommended to implement a whitelist of allowed topics or add an authorization check before forwarding the request.

Comment on lines +223 to +224
valid_keypoints = sum(1 for score in self.keypoint_scores if score > 0.5)
return valid_keypoints >= 1

Choose a reason for hiding this comment

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

medium

is_valid 메서드의 유효성 검사 조건이 크게 완화되었습니다. 키포인트 신뢰도 임계값이 0.8에서 0.5로, 필요한 키포인트 수가 5개에서 1개로 줄었습니다. 이로 인해 단 하나의 키포인트(예: 코)만 50% 신뢰도로 탐지되어도 유효한 사람으로 간주되어, 오탐(false positive)의 가능성이 크게 높아집니다. 시뮬레이션 환경을 위한 의도적인 조정일 수 있으나, 시스템의 안정성을 위해 조건을 조금 더 강화하는 것을 고려해 보시는 것이 좋겠습니다.

Suggested change
valid_keypoints = sum(1 for score in self.keypoint_scores if score > 0.5)
return valid_keypoints >= 1
valid_keypoints = sum(1 for score in self.keypoint_scores if score > 0.6)
return valid_keypoints >= 3

Copy link
Contributor Author

Choose a reason for hiding this comment

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

시뮬레이션 상에서 사람을 detection하기 위해 검사조건을 느슨하게 변경하엿습니다. 실제 go2에서 사람을 detection할때에 다시 조정할 필요가 있습니다.

from go2_interfaces.msg import WebRtcReq # type: ignore[import-not-found]

return WebRtcReq
except Exception:

Choose a reason for hiding this comment

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

medium

except Exception:은 너무 광범위한 예외 처리입니다. from go2_interfaces.msg import WebRtcReq 구문에서 발생할 수 있는 주된 예외는 ImportError이므로, 더 구체적인 예외를 명시하여 코드의 의도를 명확히 하고 예상치 못한 다른 예외가 숨겨지는 것을 방지하는 것이 좋습니다.

Suggested change
except Exception:
except ImportError:


try:
return json.loads(parameter)
except Exception:

Choose a reason for hiding this comment

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

medium

except Exception:은 너무 광범위한 예외 처리입니다. json.loads()에서 발생할 수 있는 주된 예외는 json.JSONDecodeError이므로, 더 구체적인 예외를 명시하여 코드의 의도를 명확히 하고 예상치 못한 다른 예외가 숨겨지는 것을 방지하는 것이 좋습니다.

Suggested change
except Exception:
except json.JSONDecodeError:

if isinstance(class_id_raw, str):
class_id = int(class_id_raw) if class_id_raw.isdigit() else 0
else:
class_id = int(class_id_raw)
Copy link
Member

Choose a reason for hiding this comment

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

if-else 조건문을 사용하지 않도록 하시죠. ros 에서 vision msg 에서 class_id 는 string 으로 확장성있게 변경이 되었다고 합니다. (uuid 나 namespace 적용을 위해)
따라서 string 으로 바꾸시는건 맞지만 단순히 int index 번호를 가지로 string 으로 변환하지는 않고 {class_id}:{name} 로 id 를 만들어서 주입해주시길 바랍니다

Copy link
Contributor Author

Choose a reason for hiding this comment

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

image 해당형식으로 id를 변환하였습니다!

Copy link
Member

Choose a reason for hiding this comment

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

해당 파일은 battlebang 레포로 이관 예정

Copy link

@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 (3)
dimos/protocol/pubsub/impl/rospubsub_conversion.py (1)

63-72: 현재는 string 타입만 강제 변환하지만, 향후 다른 타입 불일치가 발생할 수 있습니다.

현재 구현은 class_id와 같은 특정 케이스를 해결하지만, ROS의 다른 타입 불일치(예: float32 vs float64, int32 vs int64)가 발생할 경우 확장이 필요할 수 있습니다. 디버깅 편의를 위해 강제 변환이 발생했을 때 디버그 로그를 추가하는 것을 고려해 보세요.

🔧 선택적 개선안: 강제 변환 시 디버그 로깅 추가
+import logging
+
+_logger = logging.getLogger(__name__)
+
 def _coerce_lcm_scalar_to_ros_type(value: Any, ros_type_hint: str) -> Any:
     """Coerce scalar values to match ROS runtime field assertions.

     ROS Python message setters perform strict runtime type checks. Some LCM-backed
     messages can carry numerics in fields that ROS expects as strings (e.g.
     vision_msgs/ObjectHypothesis.class_id). Coerce those primitive mismatches here.
     """
     if ros_type_hint == "string" and not isinstance(value, str):
+        _logger.debug("Coercing %r to string for ROS field (hint=%s)", value, ros_type_hint)
         return str(value)
     return value
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dimos/protocol/pubsub/impl/rospubsub_conversion.py` around lines 63 - 72, The
helper _coerce_lcm_scalar_to_ros_type currently only coerces non-str to string;
add a debug log when any coercion occurs (e.g., include the function name,
original value, original type, and target ros_type_hint) to aid debugging, and
prepare the function to easily extend coercion rules for numeric mismatches
later (keep branching centralized in _coerce_lcm_scalar_to_ros_type so adding
float/int widening like float32->float64 or int32->int64 is straightforward).
Use the module logger (or pass a logger) to emit the debug message right before
returning the coerced value so callers like rospubsub conversion paths can see
what was changed.
dimos/perception/detection/type/detection2d/person.py (1)

223-224: 유효성 임계값은 상수/Config로 분리하는 편이 좋습니다.

Line 223-224는 튜닝 포인트(점수 기준, 최소 키포인트 수)가 하드코딩되어 있어 모델/환경 변경 시 코드 수정이 필요합니다. 클래스 상수나 Config로 올리면 실험/운영 조정이 쉬워집니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dimos/perception/detection/type/detection2d/person.py` around lines 223 -
224, The hardcoded keypoint validity check in the method using
self.keypoint_scores (currently "score > 0.5" and "valid_keypoints >= 1") should
be replaced with configurable thresholds: add class-level constants (e.g.,
KEYPOINT_SCORE_THRESHOLD, MIN_VALID_KEYPOINTS) or read values from a Config
object and use those symbols in the validity logic (replace the literal 0.5 and
1 with the new constants or config fields) so you can tune score and
minimum-keypoint criteria without changing the code.
dimos/robot/unitree/go2/blueprints/smart/unitree_go2_battlebang_bridge.py (1)

40-42: 네임스페이스 robot0 하드코딩은 재사용성을 제한합니다.

실기/멀티로봇 환경에서 동일 블루프린트를 재사용하려면 네임스페이스를 외부 설정으로 받는 편이 안전합니다.

♻️ 제안 변경
+import os
+
-_GO2_ROS_KWARGS = {"ros_robot_namespace": "robot0"}
-_ROS_ROBOT_NAMESPACE = _GO2_ROS_KWARGS["ros_robot_namespace"]
+_ROS_ROBOT_NAMESPACE = os.getenv("ROS_ROBOT_NAMESPACE", "robot0").strip("/") or "robot0"
+_GO2_ROS_KWARGS = {"ros_robot_namespace": _ROS_ROBOT_NAMESPACE}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dimos/robot/unitree/go2/blueprints/smart/unitree_go2_battlebang_bridge.py`
around lines 40 - 42, Replace the hardcoded namespace by making
_GO2_ROS_KWARGS["ros_robot_namespace"] configurable: read the namespace from an
external parameter (environment variable or a function/class init arg) with a
default of "robot0", and set _ROS_ROBOT_NAMESPACE from that value instead of the
literal; update any references to _GO2_ROS_KWARGS and _ROS_ROBOT_NAMESPACE so
callers can pass a different namespace (e.g., expose a parameter in the
blueprint constructor or a factory function and fall back to
os.environ.get("ROS_ROBOT_NAMESPACE", "robot0")).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dimos/perception/detection/type/detection2d/point.py`:
- Line 168: Detection2DPoint currently sends class_id as str(self.class_id)
which doesn't follow the decoder's expected "id:name" format, causing
from_ros_detection2d in dimos/perception/detection/type/detection2d/bbox.py to
lose the original class name; change the serialization in Detection2DPoint (the
place constructing class_id) to emit the combined "id:name" string (e.g.,
f"{self.class_id}:{self.class_name}" or equivalent using the object's class_name
field) so the decoder can restore name and id correctly.

In `@dimos/robot/unitree/go2/battlebang_webrtc_bridge.py`:
- Around line 84-106: Wrap the post-start RPC binding and subscription logic
(the block using get_rpc_calls, self._unsubs.append(...), and logger.info calls)
in a try/except/finally (or try/except that calls self._ros.stop() before
re-raising) so that if any exception occurs after RawROS.start() the ROS
resource is stopped; specifically, after calling RawROS.start() invoke the
subscription/RPC setup inside a try: and in except Exception as e: call
self._ros.stop() (and optionally clear/unsubscribe any partial entries in
self._unsubs) and re-raise the exception to preserve failure behavior—this
ensures RawROS.start() is always paired with RawROS.stop() on initialization
failure when using methods like get_rpc_calls, _on_webrtc_req_raw,
_on_cmd_vel_raw, and subscribing to RawROSTopic.
- Around line 110-114: The cleanup loop that calls each function in self._unsubs
currently swallows exceptions with a bare except/pass; update the except block
in the for unsub in getattr(self, "_unsubs", []) loop to log the failure
including exception details (e.g., using logging.exception or
self.logger.exception) and context about which unsub failed (include repr(unsub)
or its name) so cleanup errors are recorded for postmortem.

---

Nitpick comments:
In `@dimos/perception/detection/type/detection2d/person.py`:
- Around line 223-224: The hardcoded keypoint validity check in the method using
self.keypoint_scores (currently "score > 0.5" and "valid_keypoints >= 1") should
be replaced with configurable thresholds: add class-level constants (e.g.,
KEYPOINT_SCORE_THRESHOLD, MIN_VALID_KEYPOINTS) or read values from a Config
object and use those symbols in the validity logic (replace the literal 0.5 and
1 with the new constants or config fields) so you can tune score and
minimum-keypoint criteria without changing the code.

In `@dimos/protocol/pubsub/impl/rospubsub_conversion.py`:
- Around line 63-72: The helper _coerce_lcm_scalar_to_ros_type currently only
coerces non-str to string; add a debug log when any coercion occurs (e.g.,
include the function name, original value, original type, and target
ros_type_hint) to aid debugging, and prepare the function to easily extend
coercion rules for numeric mismatches later (keep branching centralized in
_coerce_lcm_scalar_to_ros_type so adding float/int widening like
float32->float64 or int32->int64 is straightforward). Use the module logger (or
pass a logger) to emit the debug message right before returning the coerced
value so callers like rospubsub conversion paths can see what was changed.

In `@dimos/robot/unitree/go2/blueprints/smart/unitree_go2_battlebang_bridge.py`:
- Around line 40-42: Replace the hardcoded namespace by making
_GO2_ROS_KWARGS["ros_robot_namespace"] configurable: read the namespace from an
external parameter (environment variable or a function/class init arg) with a
default of "robot0", and set _ROS_ROBOT_NAMESPACE from that value instead of the
literal; update any references to _GO2_ROS_KWARGS and _ROS_ROBOT_NAMESPACE so
callers can pass a different namespace (e.g., expose a parameter in the
blueprint constructor or a factory function and fall back to
os.environ.get("ROS_ROBOT_NAMESPACE", "robot0")).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.

Run ID: c0b8fd0d-e45d-41ea-b7be-e4e79071be89

📥 Commits

Reviewing files that changed from the base of the PR and between 3e03b20 and ca72e42.

📒 Files selected for processing (14)
  • dimos/perception/detection/detectors/test_bbox_detectors.py
  • dimos/perception/detection/module2D.py
  • dimos/perception/detection/type/detection2d/bbox.py
  • dimos/perception/detection/type/detection2d/person.py
  • dimos/perception/detection/type/detection2d/point.py
  • dimos/perception/detection/type/detection2d/test_imageDetections2D.py
  • dimos/protocol/pubsub/impl/rospubsub_conversion.py
  • dimos/robot/all_blueprints.py
  • dimos/robot/unitree/go2/battlebang_webrtc_bridge.py
  • dimos/robot/unitree/go2/blueprints/__init__.py
  • dimos/robot/unitree/go2/blueprints/smart/unitree_go2_battlebang_bridge.py
  • dimos/robot/unitree/go2/connection.py
  • dimos/robot/unitree/go2/ros_sim_connection.py
  • dimos/web/websocket_vis/websocket_vis_module.py

ObjectHypothesisWithPose(
ObjectHypothesis(
class_id=self.class_id,
class_id=str(self.class_id),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Detection2DPoint의 ROS class_id 인코딩 포맷이 현재 디코더 규약과 불일치합니다.

Line 168에서 class_id=str(self.class_id)만 보내면, dimos/perception/detection/type/detection2d/bbox.pyfrom_ros_detection2d 경로(디코더가 id:name 기대)에서 이름이 복원되지 않고 class_{id}로 대체됩니다. 이로 인해 ROS round-trip 시 클래스명이 손실됩니다.

🔧 제안 수정
-                        class_id=str(self.class_id),
+                        class_id=f"{self.class_id}:{self.name}",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class_id=str(self.class_id),
class_id=f"{self.class_id}:{self.name}",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dimos/perception/detection/type/detection2d/point.py` at line 168,
Detection2DPoint currently sends class_id as str(self.class_id) which doesn't
follow the decoder's expected "id:name" format, causing from_ros_detection2d in
dimos/perception/detection/type/detection2d/bbox.py to lose the original class
name; change the serialization in Detection2DPoint (the place constructing
class_id) to emit the combined "id:name" string (e.g.,
f"{self.class_id}:{self.class_name}" or equivalent using the object's class_name
field) so the decoder can restore name and id correctly.

Comment on lines +84 to +106
self._ros = RawROS(node_name=self.config.ros_node_name)
self._ros.start()

if subscribe_webrtc and webrtc_req_type is not None:
self._publish_request_rpc = self.get_rpc_calls("GO2Connection.publish_request")
self._unsubs.append(
self._ros.subscribe(
RawROSTopic(self.config.webrtc_req_topic, webrtc_req_type),
self._on_webrtc_req_raw,
)
)
logger.info(f"[BattlebangWebrtcBridge] subscribed to {self.config.webrtc_req_topic}")

if subscribe_cmd_vel:
self._move_rpc = self.get_rpc_calls("GO2Connection.move")
ros_twist_type = derive_ros_type(Twist)
self._unsubs.append(
self._ros.subscribe(
RawROSTopic(self.config.cmd_vel_topic, ros_twist_type),
self._on_cmd_vel_raw,
)
)
logger.info(f"[BattlebangWebrtcBridge] subscribed to {self.config.cmd_vel_topic}")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

초기화 중간 실패 시 ROS 리소스 누수가 발생할 수 있습니다.

RawROS.start() 이후 RPC 바인딩/구독 단계에서 예외가 나면 정리 없이 start()가 종료될 수 있습니다. 실패 경로에서 self._ros.stop() 보장이 필요합니다.

🐛 제안 변경
-        self._ros = RawROS(node_name=self.config.ros_node_name)
-        self._ros.start()
-
         if subscribe_webrtc and webrtc_req_type is not None:
             self._publish_request_rpc = self.get_rpc_calls("GO2Connection.publish_request")
-            self._unsubs.append(
-                self._ros.subscribe(
-                    RawROSTopic(self.config.webrtc_req_topic, webrtc_req_type),
-                    self._on_webrtc_req_raw,
-                )
-            )
-            logger.info(f"[BattlebangWebrtcBridge] subscribed to {self.config.webrtc_req_topic}")
 
         if subscribe_cmd_vel:
             self._move_rpc = self.get_rpc_calls("GO2Connection.move")
-            ros_twist_type = derive_ros_type(Twist)
-            self._unsubs.append(
-                self._ros.subscribe(
-                    RawROSTopic(self.config.cmd_vel_topic, ros_twist_type),
-                    self._on_cmd_vel_raw,
-                )
-            )
-            logger.info(f"[BattlebangWebrtcBridge] subscribed to {self.config.cmd_vel_topic}")
+        self._ros = RawROS(node_name=self.config.ros_node_name)
+        try:
+            self._ros.start()
+
+            if subscribe_webrtc and webrtc_req_type is not None:
+                self._unsubs.append(
+                    self._ros.subscribe(
+                        RawROSTopic(self.config.webrtc_req_topic, webrtc_req_type),
+                        self._on_webrtc_req_raw,
+                    )
+                )
+                logger.info(f"[BattlebangWebrtcBridge] subscribed to {self.config.webrtc_req_topic}")
+
+            if subscribe_cmd_vel:
+                ros_twist_type = derive_ros_type(Twist)
+                self._unsubs.append(
+                    self._ros.subscribe(
+                        RawROSTopic(self.config.cmd_vel_topic, ros_twist_type),
+                        self._on_cmd_vel_raw,
+                    )
+                )
+                logger.info(f"[BattlebangWebrtcBridge] subscribed to {self.config.cmd_vel_topic}")
+        except Exception:
+            if self._ros is not None:
+                self._ros.stop()
+                self._ros = None
+            raise
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dimos/robot/unitree/go2/battlebang_webrtc_bridge.py` around lines 84 - 106,
Wrap the post-start RPC binding and subscription logic (the block using
get_rpc_calls, self._unsubs.append(...), and logger.info calls) in a
try/except/finally (or try/except that calls self._ros.stop() before re-raising)
so that if any exception occurs after RawROS.start() the ROS resource is
stopped; specifically, after calling RawROS.start() invoke the subscription/RPC
setup inside a try: and in except Exception as e: call self._ros.stop() (and
optionally clear/unsubscribe any partial entries in self._unsubs) and re-raise
the exception to preserve failure behavior—this ensures RawROS.start() is always
paired with RawROS.stop() on initialization failure when using methods like
get_rpc_calls, _on_webrtc_req_raw, _on_cmd_vel_raw, and subscribing to
RawROSTopic.

Comment on lines +110 to +114
for unsub in getattr(self, "_unsubs", []):
try:
unsub()
except Exception:
pass
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

구독 해제 예외를 무시하지 말고 로깅해 주세요.

정리 실패를 pass로 삼키면 운영 중 장애 원인 추적이 어려워집니다.

🛠️ 제안 변경
         for unsub in getattr(self, "_unsubs", []):
             try:
                 unsub()
-            except Exception:
-                pass
+            except Exception as e:
+                logger.exception(f"[BattlebangWebrtcBridge] unsubscribe failed: {e}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for unsub in getattr(self, "_unsubs", []):
try:
unsub()
except Exception:
pass
for unsub in getattr(self, "_unsubs", []):
try:
unsub()
except Exception as e:
logger.exception(f"[BattlebangWebrtcBridge] unsubscribe failed: {e}")
🧰 Tools
🪛 Ruff (0.15.2)

[error] 113-114: try-except-pass detected, consider logging the exception

(S110)


[warning] 113-113: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dimos/robot/unitree/go2/battlebang_webrtc_bridge.py` around lines 110 - 114,
The cleanup loop that calls each function in self._unsubs currently swallows
exceptions with a bare except/pass; update the except block in the for unsub in
getattr(self, "_unsubs", []) loop to log the failure including exception details
(e.g., using logging.exception or self.logger.exception) and context about which
unsub failed (include repr(unsub) or its name) so cleanup errors are recorded
for postmortem.

track_id = int(str(ros_det.id))
except (TypeError, ValueError):
track_id = 0

Copy link
Member

Choose a reason for hiding this comment

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

제가 기억하기로는 track_id 는 dimos 에 자체 tracking 을 하기 위한 id 로 string 으로 되어있던데 괜찮은 건가요??

Copy link
Member

Choose a reason for hiding this comment

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

이 파일 및 battlebang 과 관련된 것들은 더 이상 수정하지 말고 일단 simulation 에서 돌아가게끔 하고 battlebang 이 refactor 완료되면 그 때 하는걸로 하시죠~

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