Files
ros_contorl/scripts/convert_flexiv_traj.py
T
arm_dev 6edb8d39ab 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 / 模型权重二进制。
2026-09-23 10:53:06 +08:00

118 lines
4.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""Convert Flexiv Elements .traj (text) -> arm_application YAML waypoints."""
from __future__ import annotations
import argparse
import math
import re
import sys
from pathlib import Path
from typing import Any
def parse_flexiv_traj(text: str) -> list[dict[str, Any]]:
"""Parse Elements Studio scatter-point .traj file."""
blocks = re.split(r'pattern_params\s*\{', text)[1:]
waypoints: list[dict[str, Any]] = []
for block in blocks:
name_m = re.search(r'pattern_name:\s*"([^"]+)"', block)
vel_m = re.search(r'velocity:\s*"([^"]+)"', block)
joints: dict[str, float] = {}
for j in range(1, 8):
m = re.search(rf'joint{j}:\s*"([^"]+)"', block)
if m:
joints[f'joint{j}'] = math.radians(float(m.group(1)))
if len(joints) != 7:
continue
positions = [joints[f'joint{i}'] for i in range(1, 8)]
waypoints.append({
'name': name_m.group(1) if name_m else f'WPT{len(waypoints)}',
'positions': positions,
'velocity': float(vel_m.group(1)) if vel_m else 0.2,
})
return waypoints
def assign_times(waypoints: list[dict[str, Any]], min_seg: float = 3.0) -> None:
"""Cumulative time_from_start from joint-space segment length."""
t = 0.0
prev = None
for wp in waypoints:
pos = wp['positions']
if prev is None:
t += min_seg
else:
delta = max(abs(a - b) for a, b in zip(pos, prev))
# ~4 s per radian max joint move, floored at min_seg
t += max(min_seg, delta * 4.0)
wp['time'] = round(t, 2)
prev = pos
def to_yaml(
waypoints: list[dict[str, Any]],
*,
joint_prefix: str = 'Rizon4-063989_',
velocity_scale: float | None = None,
) -> str:
if velocity_scale is None:
velocity_scale = waypoints[0].get('velocity', 0.2) if waypoints else 0.2
times = [wp['time'] for wp in waypoints]
flat: list[float] = []
for wp in waypoints:
flat.extend(wp['positions'])
pos_line = ', '.join(f'{v:.6f}' for v in flat)
time_line = ', '.join(str(t) for t in times)
names = ', '.join(f' # {wp.get("name", "")}' if i == 0 else '' for i, wp in enumerate(waypoints))
_ = names # names kept in comments below
lines = [
'# Auto-converted from Flexiv Elements .traj (joints: deg -> rad)',
'# ROS params: flat waypoint_positions + waypoint_times (no nested dicts)',
'# Edit velocity_scale / loop_count as needed (no rebuild).',
'run_joint_trajectory:',
' ros__parameters:',
f' joint_prefix: "{joint_prefix}"',
' joint_names: [joint1, joint2, joint3, joint4, joint5, joint6, joint7]',
f' velocity_scale: {velocity_scale}',
' loop_count: 1 # 1=once, 0=forever, N=repeat N',
' pause_between_loops: 0.5',
f' waypoint_times: [{time_line}]',
' waypoint_positions: [',
]
for i, wp in enumerate(waypoints):
pos = ', '.join(f'{v:.6f}' for v in wp['positions'])
lines.append(f' {pos}, # {wp.get("name", f"WPT{i}")}')
lines.append(' ]')
return '\n'.join(lines) + '\n'
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument('input', type=Path, help='Flexiv Elements .traj file')
ap.add_argument('-o', '--output', type=Path, help='Output YAML path')
ap.add_argument('--prefix', default='Rizon4-063989_', help='Joint name prefix (site SN + _)')
ap.add_argument('--min-seg', type=float, default=3.0, help='Min seconds per segment')
args = ap.parse_args()
text = args.input.read_text(encoding='utf-8')
wps = parse_flexiv_traj(text)
if not wps:
print('ERROR: no waypoints parsed', file=sys.stderr)
return 1
assign_times(wps, min_seg=args.min_seg)
yaml_text = to_yaml(wps, joint_prefix=args.prefix)
out = args.output
if out:
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(yaml_text, encoding='utf-8')
print(f'Wrote {len(wps)} waypoints -> {out}')
else:
print(yaml_text)
return 0
if __name__ == '__main__':
raise SystemExit(main())