feat: integrate Yahboom with shared Nav2 modules and CAD visuals

This commit is contained in:
2026-09-22 11:11:54 +09:00
parent 155a6ff215
commit 4ad97ba5d8
13 changed files with 1997 additions and 3 deletions
+1
View File
@@ -2,6 +2,7 @@
"sourceType": "GIT",
"name": "NAV2-Yahboom",
"category": "Vehicle",
"defaultParameters": ["sim_mode:=true"],
"dockerImage": "future-nav2:humble-yahboom",
"packageName": "yahboom_vehicle",
"launchFile": "vehicle.launch.py"
+4 -1
View File
@@ -6,7 +6,7 @@ LMS 전용 템플릿 저장소입니다. 설치 폴더: `[lms-nav2-yahboom-vehic
- 런치: `vehicle.launch.py`
- 이미지: `future-nav2:humble-yahboom` (실차 기본)
- 로컬 시뮬레이션: 빌드 전에 `sim_mode:=true`를 설정하면 LMS가
`future-nav2-gazebo:humble`을 사용합니다. 기본값은 `false`입니다.
`future-nav2-gazebo:humble`을 사용합니다. 기본값은 `true`이며 실차에서는 `sim_mode:=false`를 명시합니다.
- 시뮬레이션에서는 기존 NAV2-Gazebo에 `platform:=yahboom`을 설정하고
NAV2-Yahboom 차량 모듈을 함께 실행합니다. 실차 Agent/Camera는 실행하지 않습니다.
- 앱의 야붐 실행기 지원이 반영되어야 합니다.
@@ -15,6 +15,9 @@ LMS 전용 템플릿 저장소입니다. 설치 폴더: `[lms-nav2-yahboom-vehic
- ROS_DOMAIN_ID는 ESP32 설정과 일치시킵니다. GPU 가속은 끕니다.
- 컨테이너 장치: 없음
- SLAM과 Localization은 동시에 실행하지 않습니다.
- `/vehicle_config`를 발행합니다 (`robot=yahboom`, `level=2d`, `sim_mode`).
야붐 설정이 추가된 공통 NAV2-SLAM, NAV2-Map, NAV2-Localization,
NAV2-Planning, NAV2-Control을 사용합니다. 전용 Yahboom 주행 모듈과 중복 실행하지 않습니다.
- 실차 검증 전 소스입니다. 바퀴를 띄우고 비상정지와 명령 타임아웃을 확인하세요.
원본 소스: https://github.com/KingKiser/hkit_yahboom_ws
+1 -1
View File
@@ -2,5 +2,5 @@ cmake_minimum_required(VERSION 3.8)
project(yahboom_vehicle)
find_package(ament_cmake REQUIRED)
install(DIRECTORY launch urdf meshes DESTINATION share/${PROJECT_NAME})
install(PROGRAMS scripts/odom_bridge.py DESTINATION lib/${PROJECT_NAME})
install(PROGRAMS scripts/odom_bridge.py scripts/vehicle_config.py DESTINATION lib/${PROJECT_NAME})
ament_package()
+3 -1
View File
@@ -16,6 +16,8 @@ def launch_vehicle(context):
if simulation:
description = runpy.run_path(os.path.join(share, "urdf", "simulation.py"))["simulation_description"](share)
actions = [
Node(package="yahboom_vehicle", executable="vehicle_config.py",
parameters=[{"sim_mode": simulation}], output="screen"),
Node(package="robot_state_publisher", executable="robot_state_publisher",
parameters=[{"robot_description": description, "use_sim_time": simulation}],
remappings=[("robot_description", "/yahboom/robot_description")] if simulation else [],
@@ -30,7 +32,7 @@ def launch_vehicle(context):
def generate_launch_description():
return LaunchDescription([
DeclareLaunchArgument("sim_mode", default_value="false", choices=["true", "false"]),
DeclareLaunchArgument("sim_mode", default_value="true", choices=["true", "false"]),
DeclareLaunchArgument("publish_odom_tf", default_value="true"),
OpaqueFunction(function=launch_vehicle),
])
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
{
"bounds_ros_m": [
[
-0.16315217316150665,
-0.07669481635093689,
-9.201839566230774e-05
],
[
0.12379159778356552,
0.07669482380151749,
0.13575001060962677
]
],
"parts": {
"yahboom_body": {
"vertices": 151771,
"faces": 91337
},
"yahboom_front_left": {
"vertices": 2606,
"faces": 2266
},
"yahboom_front_right": {
"vertices": 2607,
"faces": 2267
},
"yahboom_rear_left": {
"vertices": 2610,
"faces": 2267
},
"yahboom_rear_right": {
"vertices": 2608,
"faces": 2266
}
},
"alignment": "STEP Z-up: rotate Z pi, translate X -0.002m; base_footprint Z offset 0.04525m"
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -10,6 +10,7 @@
<exec_depend>launch_ros</exec_depend>
<exec_depend>ament_index_python</exec_depend>
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>nav_msgs</exec_depend>
<exec_depend>tf2_ros</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
"""Publish the shared NAV2 module configuration, including for late subscribers."""
import json
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, DurabilityPolicy, ReliabilityPolicy
from std_msgs.msg import String
class VehicleConfig(Node):
def __init__(self):
super().__init__('yahboom_vehicle_config')
self.declare_parameter('sim_mode', True)
qos = QoSProfile(depth=1, durability=DurabilityPolicy.TRANSIENT_LOCAL,
reliability=ReliabilityPolicy.RELIABLE)
self.publisher = self.create_publisher(String, '/vehicle_config', qos)
config = {'robot': 'yahboom', 'platform': 'yahboom', 'level': '2d',
'sim_mode': self.get_parameter('sim_mode').value}
self.publisher.publish(String(data=json.dumps(config)))
self.get_logger().info(f'Published vehicle configuration: {config}')
def main():
rclpy.init()
node = VehicleConfig()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
if rclpy.ok():
rclpy.shutdown()
if __name__ == '__main__':
main()
+11
View File
@@ -5,6 +5,17 @@ import xml.etree.ElementTree as ET
def simulation_description(share):
root = ET.parse(Path(share) / "urdf" / "yahboom.urdf").getroot()
# Only replace visuals. Keep the validated collision/inertial/sensor model.
visuals = {"base_link": "body", "zq_Link": "front_left", "yq_Link": "front_right",
"zh_Link": "rear_left", "yh_Link": "rear_right"}
for link in root.findall("link"):
for visual in link.findall("visual"):
link.remove(visual)
part = visuals.get(link.get("name"))
if part:
visual = ET.SubElement(link, "visual")
geometry = ET.SubElement(visual, "geometry")
ET.SubElement(geometry, "mesh", filename=f"package://yahboom_vehicle/meshes/pi5/{part}.dae")
for mesh in root.findall(".//mesh"):
mesh.set("filename", (Path(share) / mesh.get("filename").split("package://yahboom_vehicle/")[1]).as_uri())
for joint in root.findall("joint"):