feat: integrate Yahboom with shared Nav2 modules and CAD visuals
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
"sourceType": "GIT",
|
"sourceType": "GIT",
|
||||||
"name": "NAV2-Yahboom",
|
"name": "NAV2-Yahboom",
|
||||||
"category": "Vehicle",
|
"category": "Vehicle",
|
||||||
|
"defaultParameters": ["sim_mode:=true"],
|
||||||
"dockerImage": "future-nav2:humble-yahboom",
|
"dockerImage": "future-nav2:humble-yahboom",
|
||||||
"packageName": "yahboom_vehicle",
|
"packageName": "yahboom_vehicle",
|
||||||
"launchFile": "vehicle.launch.py"
|
"launchFile": "vehicle.launch.py"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ LMS 전용 템플릿 저장소입니다. 설치 폴더: `[lms-nav2-yahboom-vehic
|
|||||||
- 런치: `vehicle.launch.py`
|
- 런치: `vehicle.launch.py`
|
||||||
- 이미지: `future-nav2:humble-yahboom` (실차 기본)
|
- 이미지: `future-nav2:humble-yahboom` (실차 기본)
|
||||||
- 로컬 시뮬레이션: 빌드 전에 `sim_mode:=true`를 설정하면 LMS가
|
- 로컬 시뮬레이션: 빌드 전에 `sim_mode:=true`를 설정하면 LMS가
|
||||||
`future-nav2-gazebo:humble`을 사용합니다. 기본값은 `false`입니다.
|
`future-nav2-gazebo:humble`을 사용합니다. 기본값은 `true`이며 실차에서는 `sim_mode:=false`를 명시합니다.
|
||||||
- 시뮬레이션에서는 기존 NAV2-Gazebo에 `platform:=yahboom`을 설정하고
|
- 시뮬레이션에서는 기존 NAV2-Gazebo에 `platform:=yahboom`을 설정하고
|
||||||
NAV2-Yahboom 차량 모듈을 함께 실행합니다. 실차 Agent/Camera는 실행하지 않습니다.
|
NAV2-Yahboom 차량 모듈을 함께 실행합니다. 실차 Agent/Camera는 실행하지 않습니다.
|
||||||
- 앱의 야붐 실행기 지원이 반영되어야 합니다.
|
- 앱의 야붐 실행기 지원이 반영되어야 합니다.
|
||||||
@@ -15,6 +15,9 @@ LMS 전용 템플릿 저장소입니다. 설치 폴더: `[lms-nav2-yahboom-vehic
|
|||||||
- ROS_DOMAIN_ID는 ESP32 설정과 일치시킵니다. GPU 가속은 끕니다.
|
- ROS_DOMAIN_ID는 ESP32 설정과 일치시킵니다. GPU 가속은 끕니다.
|
||||||
- 컨테이너 장치: 없음
|
- 컨테이너 장치: 없음
|
||||||
- SLAM과 Localization은 동시에 실행하지 않습니다.
|
- 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
|
원본 소스: https://github.com/KingKiser/hkit_yahboom_ws
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ cmake_minimum_required(VERSION 3.8)
|
|||||||
project(yahboom_vehicle)
|
project(yahboom_vehicle)
|
||||||
find_package(ament_cmake REQUIRED)
|
find_package(ament_cmake REQUIRED)
|
||||||
install(DIRECTORY launch urdf meshes DESTINATION share/${PROJECT_NAME})
|
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()
|
ament_package()
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ def launch_vehicle(context):
|
|||||||
if simulation:
|
if simulation:
|
||||||
description = runpy.run_path(os.path.join(share, "urdf", "simulation.py"))["simulation_description"](share)
|
description = runpy.run_path(os.path.join(share, "urdf", "simulation.py"))["simulation_description"](share)
|
||||||
actions = [
|
actions = [
|
||||||
|
Node(package="yahboom_vehicle", executable="vehicle_config.py",
|
||||||
|
parameters=[{"sim_mode": simulation}], output="screen"),
|
||||||
Node(package="robot_state_publisher", executable="robot_state_publisher",
|
Node(package="robot_state_publisher", executable="robot_state_publisher",
|
||||||
parameters=[{"robot_description": description, "use_sim_time": simulation}],
|
parameters=[{"robot_description": description, "use_sim_time": simulation}],
|
||||||
remappings=[("robot_description", "/yahboom/robot_description")] if simulation else [],
|
remappings=[("robot_description", "/yahboom/robot_description")] if simulation else [],
|
||||||
@@ -30,7 +32,7 @@ def launch_vehicle(context):
|
|||||||
|
|
||||||
def generate_launch_description():
|
def generate_launch_description():
|
||||||
return LaunchDescription([
|
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"),
|
DeclareLaunchArgument("publish_odom_tf", default_value="true"),
|
||||||
OpaqueFunction(function=launch_vehicle),
|
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
@@ -10,6 +10,7 @@
|
|||||||
<exec_depend>launch_ros</exec_depend>
|
<exec_depend>launch_ros</exec_depend>
|
||||||
<exec_depend>ament_index_python</exec_depend>
|
<exec_depend>ament_index_python</exec_depend>
|
||||||
<exec_depend>rclpy</exec_depend>
|
<exec_depend>rclpy</exec_depend>
|
||||||
|
<exec_depend>std_msgs</exec_depend>
|
||||||
<exec_depend>nav_msgs</exec_depend>
|
<exec_depend>nav_msgs</exec_depend>
|
||||||
<exec_depend>tf2_ros</exec_depend>
|
<exec_depend>tf2_ros</exec_depend>
|
||||||
<exec_depend>robot_state_publisher</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()
|
||||||
@@ -5,6 +5,17 @@ import xml.etree.ElementTree as ET
|
|||||||
|
|
||||||
def simulation_description(share):
|
def simulation_description(share):
|
||||||
root = ET.parse(Path(share) / "urdf" / "yahboom.urdf").getroot()
|
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"):
|
for mesh in root.findall(".//mesh"):
|
||||||
mesh.set("filename", (Path(share) / mesh.get("filename").split("package://yahboom_vehicle/")[1]).as_uri())
|
mesh.set("filename", (Path(share) / mesh.get("filename").split("package://yahboom_vehicle/")[1]).as_uri())
|
||||||
for joint in root.findall("joint"):
|
for joint in root.findall("joint"):
|
||||||
|
|||||||
Reference in New Issue
Block a user