Files
lightweight-cobot/src/iiwa_bringup/launch/supported/webots_spawn.launch.py
T
Даниил Грабарь 3a5bc28bba feat: add release-to-move TCP gizmo control in Webots
Add a native Webots marker for setting the active tool's TCP position and
orientation. Dragging edits the preview; releasing the mouse submits one
complete, checked joint trajectory with configured TCP speed limits.
Unreachable targets reset the marker to the current TCP.

Preserve coordinate, joint, and control-file commands through motion
reservations and a controller action proxy. Give the gizmo lowest priority
and finish its committed trajectory before dispatching queued regular
commands, including waits longer than the former two-second service timeout.

Place the patron TCP at its working end, 55.2 mm along local Z, and use the
flange TCP when no tool is attached. Add digital_twin.tcp_gizmo configuration,
ROS interfaces, launch integration, and regression coverage.

Document operation, configuration, tool attachment, speed limits, and
troubleshooting in both Russian and English.

Validation: 42 Python tests and 8 C++ tests passed. Webots regressions covered
one action per release, translation and rotation with and without a tool,
unreachable targets, and ordinary commands waiting for gizmo completion.
2026-09-17 17:11:53 +03:00

93 lines
3.3 KiB
Python

import tempfile
import yaml
from launch import LaunchDescription
from launch.actions import (
EmitEvent,
OpaqueFunction,
RegisterEventHandler,
)
from launch.event_handlers import OnProcessExit
from launch.events import Shutdown
from launch.substitutions import LaunchConfiguration
from webots_ros2_driver.webots_controller import WebotsController
from webots_ros2_driver.webots_launcher import WebotsLauncher
from iiwa_utils import converter
def _spawn_setup(context, *args, **kwargs):
robot_name = LaunchConfiguration("robot_name").perform(context)
description = LaunchConfiguration("description").perform(context)
world = LaunchConfiguration("world").perform(context)
controller = LaunchConfiguration("controller").perform(context)
initial_positions_file = LaunchConfiguration("initial_positions_file").perform(context)
gizmo_enabled = LaunchConfiguration("tcp_gizmo_enabled", default="false").perform(context) == "true"
robot_description = converter.load_robot_description(
model_path=description,
robot_name=robot_name,
xacro_args={
"simulate": "true",
"initial_positions_file": initial_positions_file,
},
)
# robot_description cannot be passed via -p key:=value: rcl's YAML parser
# chokes on XML content (quotes, angle brackets). Merge all params into one
# temp file and pass via --params-file. A minimal dict triggers --ros-args
# so WebotsController includes the required prefix in the command.
with open(controller, "r") as f:
controller_cfg = yaml.safe_load(f) or {}
# FRI sets the hardware cycle dynamically in controllers.launch.py. Webots
# has no FRI period, so preserve its established 200 Hz controller cycle.
manager_params = controller_cfg.setdefault("controller_manager", {}).setdefault(
"ros__parameters", {}
)
manager_params["update_rate"] = 200
combined = {
"/**": {"ros__parameters": {
"robot_description": robot_description,
"set_robot_state_publisher": False,
}}
}
combined.update(controller_cfg)
with tempfile.NamedTemporaryFile(
mode="w", suffix=".yaml", delete=False, prefix="iiwa7_webots_"
) as f:
yaml.dump(combined, f, default_flow_style=False, allow_unicode=True)
params_file = f.name
webots = WebotsLauncher(world=world, ros2_supervisor=True)
driver = WebotsController(
robot_name=robot_name,
remappings=[
('/iiwa_arm_controller/joint_trajectory', '/cobot/tcp_gizmo/controller_command'),
('/iiwa_arm_controller/follow_joint_trajectory', '/cobot/tcp_gizmo/follow_joint_trajectory'),
] if gizmo_enabled else [],
parameters=[
{"use_sim_time": True}, # dict → triggers --ros-args prefix
params_file, # file → --params-file (robot_description + controllers)
],
respawn=True,
)
shutdown_on_webots_exit = RegisterEventHandler(
OnProcessExit(target_action=webots, on_exit=[EmitEvent(event=Shutdown())])
)
return [
webots,
webots._supervisor,
driver,
shutdown_on_webots_exit,
]
def generate_launch_description():
return LaunchDescription([OpaqueFunction(function=_spawn_setup)])