chore: 框架科学优化前的完整基线

在开始'设备契约层 + 目录重构 + 通止规装配'改造之前,先钉住当前可运行状态,
使后续任何删除/重构都可回退。

本基线包含:
- 现有 ROS 2 工作区全部源码(arm_application / arm_control / arm_bringup 等)
- 官方 flexiv_ros2 栈(含 flexiv_hardware / flexiv_controllers 等符号链接)
- 设备契约层 src/arm_interfaces(新增,46 项单测通过)
- devices/ 设备选型与品牌清单(新增)
- scripts/gen_site_example.py 与由它生成的 config/site.example.yaml

按 .gitignore 排除:build/ / install/ / log/ / config/site.yaml / 模型权重二进制。
This commit is contained in:
arm_dev
2026-09-23 10:53:06 +08:00
commit 6edb8d39ab
635 changed files with 1165230 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.8)
project(arm_bringup)
find_package(ament_cmake REQUIRED)
install(DIRECTORY launch config
DESTINATION share/${PROJECT_NAME}
)
ament_package()
+14
View File
@@ -0,0 +1,14 @@
# Site defaults for Flexiv Rizon 4 (SN: Rizon4-063989)
robot_sn: Rizon4-063989
rizon_type: Rizon4
# Flexiv Studio gripper (GN01) — keep false. DH PGEA uses run_gripper.sh.
load_gripper: false
use_fake_hardware: false
start_rviz: true
# Official Flexiv joint naming: Rizon4-063989_joint1 ... joint7
joint_prefix: Rizon4-063989_
base_frame: base_link
ee_frame: tcp
flange_frame: flange
+77
View File
@@ -0,0 +1,77 @@
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, LogInfo, OpaqueFunction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
from ament_index_python.packages import get_package_share_directory, PackageNotFoundError
import os
# Site robot — DH PGEA is a separate Modbus node (run_gripper.sh).
# Keep Flexiv load_gripper false (that flag is Flexiv-GN01 in Studio).
SITE_ROBOT_SN = os.environ.get('SITE_ROBOT_SN', 'Rizon4-063989')
def _launch_setup(context, *args, **kwargs):
robot_sn = LaunchConfiguration('robot_sn').perform(context)
rizon_type = LaunchConfiguration('rizon_type').perform(context)
use_fake = LaunchConfiguration('use_fake_hardware').perform(context)
load_gripper = LaunchConfiguration('load_gripper').perform(context)
start_rviz = LaunchConfiguration('start_rviz').perform(context)
if not robot_sn:
return [
LogInfo(msg=f'[arm_bringup] ERROR: robot_sn is required, e.g. robot_sn:={SITE_ROBOT_SN}')
]
try:
get_package_share_directory('flexiv_bringup')
except PackageNotFoundError:
return [
LogInfo(
msg=(
'[arm_bringup] flexiv_bringup not found. '
'Run scripts/fetch_flexiv.sh and build, or use sim.launch.py for mock.'
)
)
]
return [
LogInfo(
msg=(
f'[arm_bringup] Starting Flexiv {rizon_type} ({robot_sn}), '
f'load_gripper={load_gripper}'
)
),
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([
FindPackageShare('flexiv_bringup'), 'launch', 'rizon.launch.py'
])
),
launch_arguments={
'robot_sn': robot_sn,
'rizon_type': rizon_type,
'use_fake_hardware': use_fake,
'load_gripper': load_gripper,
'start_rviz': start_rviz,
}.items(),
),
]
def generate_launch_description():
"""Real Rizon4 bringup via official flexiv_bringup (RDK v1 / humble-v1)."""
return LaunchDescription([
DeclareLaunchArgument(
'robot_sn',
default_value=SITE_ROBOT_SN,
description='Site robot serial number (no spaces)',
),
DeclareLaunchArgument('rizon_type', default_value='Rizon4'),
DeclareLaunchArgument('use_fake_hardware', default_value='false'),
# Flexiv-GN01 only. DH PGEA: bash ~/ros2/scripts/run_gripper.sh
DeclareLaunchArgument('load_gripper', default_value='false'),
DeclareLaunchArgument('start_rviz', default_value='true'),
OpaqueFunction(function=_launch_setup),
])
+46
View File
@@ -0,0 +1,46 @@
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
"""Offline mock bringup for Flexiv Rizon 4 (7-DOF), no real robot required."""
use_rviz = LaunchConfiguration('use_rviz')
use_moveit = LaunchConfiguration('use_moveit')
robot_sn = LaunchConfiguration('robot_sn')
mock = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([
FindPackageShare('arm_control'), 'launch', 'mock_control.launch.py'
])
),
launch_arguments={
'use_rviz': use_rviz,
'robot_sn': robot_sn,
}.items(),
)
moveit = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([
FindPackageShare('arm_moveit_config'), 'launch', 'move_group.launch.py'
])
),
condition=IfCondition(use_moveit),
)
return LaunchDescription([
DeclareLaunchArgument('use_rviz', default_value='true'),
DeclareLaunchArgument('use_moveit', default_value='false'),
DeclareLaunchArgument(
'robot_sn',
default_value='Rizon4-063989',
description='Site SN; joints become Rizon4-063989_joint1..joint7',
),
mock,
moveit,
])
+208
View File
@@ -0,0 +1,208 @@
"""Unified stack launch: backend:=mock|moveit|real|isaac
Application interfaces stay the same across backends:
/set_named_pose, /get_arm_state, /arm_status, /follow_joint_trajectory
"""
import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, LogInfo, OpaqueFunction
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
from ament_index_python.packages import PackageNotFoundError, get_package_share_directory
SITE_SN = os.environ.get('SITE_ROBOT_SN', 'Rizon4-063989')
SITE_PREFIX = os.environ.get('SITE_JOINT_PREFIX', f'{SITE_SN}_')
SITE_NAMED_POSES = os.environ.get('SITE_NAMED_POSES_FILE', '').strip()
def _setup(context, *args, **kwargs):
backend = LaunchConfiguration('backend').perform(context).strip().lower()
use_rviz = LaunchConfiguration('use_rviz')
robot_sn = LaunchConfiguration('robot_sn')
load_gripper = LaunchConfiguration('load_gripper')
sn = robot_sn.perform(context).strip() or SITE_SN
actions = [
LogInfo(msg=f'[arm_bringup] backend={backend} sn={sn}'),
]
if backend in ('mock', 'moveit', 'isaac'):
actions.append(
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([
FindPackageShare('arm_control'),
'launch',
'mock_control.launch.py',
])
),
launch_arguments={
'use_rviz': use_rviz,
'robot_sn': robot_sn,
'load_gripper': load_gripper,
}.items(),
)
)
if backend == 'moveit':
actions.append(
Node(
package='arm_control',
executable='trajectory_bridge',
name='trajectory_bridge',
parameters=[{
'control_action': 'rizon_arm_controller/follow_joint_trajectory',
'arm_action': 'follow_joint_trajectory',
}],
output='screen',
)
)
actions.append(
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([
FindPackageShare('arm_moveit_config'),
'launch',
'move_group.launch.py',
])
),
launch_arguments={
'use_fake_hardware': 'true',
'robot_sn': robot_sn,
'start_rviz': 'false',
}.items(),
)
)
actions.append(
LogInfo(
msg=(
'[arm_bringup] MoveIt on top of mock. '
'Named poses still via /set_named_pose; '
'MoveIt plans execute through trajectory_bridge.'
)
)
)
if backend == 'real':
try:
get_package_share_directory('flexiv_bringup')
except PackageNotFoundError:
actions.append(
LogInfo(
msg=(
'[arm_bringup] flexiv_bringup missing. '
'Run: ./scripts/fetch_flexiv.sh && rebuild. '
'Falling back tip: use backend:=mock for now.'
)
)
)
return actions
actions.append(
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([
FindPackageShare('arm_bringup'),
'launch',
'real.launch.py',
])
),
launch_arguments={
'robot_sn': robot_sn,
'load_gripper': load_gripper,
'start_rviz': use_rviz,
}.items(),
)
)
# Keep application services on top of real HW when possible
actions.append(
Node(
package='arm_control',
executable='real_trajectory_adapter',
name='real_trajectory_adapter',
parameters=[{
'control_action': 'rizon_arm_controller/follow_joint_trajectory',
'arm_action': 'follow_joint_trajectory',
}],
output='screen',
)
)
poses_params = []
if SITE_NAMED_POSES and os.path.isfile(SITE_NAMED_POSES):
poses_params.append(SITE_NAMED_POSES)
else:
poses_params.append(
PathJoinSubstitution([
FindPackageShare('arm_control'), 'config', 'named_poses.yaml'
])
)
poses_params.append({'joint_prefix': f'{sn}_'})
actions.append(
Node(
package='arm_control',
executable='named_pose_server',
name='named_pose_server',
parameters=poses_params,
output='screen',
)
)
actions.append(
Node(
package='arm_control',
executable='arm_status_node',
name='arm_status_node',
parameters=[{
'base_frame': f'{sn}_base_link',
'ee_frame': f'{sn}_flange',
'tcp_pose_topic': f'/{sn.replace("-", "_")}/tcp_pose',
}],
output='screen',
)
)
actions.append(
LogInfo(
msg=(
'[arm_bringup] REAL mode: flexiv_bringup + named_pose_server '
'+ arm_status_node. '
'Ensure RDK is enabled and network reaches the robot.'
)
)
)
if backend == 'isaac':
actions.append(
LogInfo(
msg=(
'[arm_bringup] ISAAC mode: mock ROS stack is up for bridge testing. '
'Start Isaac Sim + flexiv_sim_plugin separately '
'(see docs/INTERFACES.md). '
'Sync joints via /joint_states or RDK bridge topics.'
)
)
)
if backend not in ('mock', 'moveit', 'real', 'isaac'):
actions.append(
LogInfo(msg=f'[arm_bringup] Unknown backend "{backend}". Use mock|moveit|real|isaac')
)
return actions
def generate_launch_description():
return LaunchDescription([
DeclareLaunchArgument(
'backend',
default_value='mock',
description='mock | moveit | real | isaac',
),
DeclareLaunchArgument('use_rviz', default_value='true'),
DeclareLaunchArgument('robot_sn', default_value=SITE_SN),
DeclareLaunchArgument('load_gripper', default_value='false'),
OpaqueFunction(function=_setup),
])
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>arm_bringup</name>
<version>0.1.0</version>
<description>Bringup for Flexiv Rizon 4 simulation and real robot.</description>
<maintainer email="dev@example.com">arm_dev</maintainer>
<license>Apache-2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<exec_depend>arm_description</exec_depend>
<exec_depend>arm_control</exec_depend>
<exec_depend>arm_moveit_config</exec_depend>
<exec_depend>arm_application</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>