ROS2 execution utilities for HPP-generated trajectories.
You write your HPP planning script using pyhpp directly, then use hpp_exec to send the trajectory to ros2_control.
The official tutorials for this package are in hpp-tutorial:
- Tutorial 6: Plan a simple arm motion and execute on Gazebo via
send_trajectory() - Tutorial 7: Pick-and-place with graph segments, gripper actions, and
execute_segments()
- Generated Doxygen documentation: user reference for the API, execution model, and troubleshooting.
After planning and time parameterization with HPP, you have a Path object that maps time (seconds) to robot configurations. For a plain arm trajectory, sample it at regular intervals to get configs:
import numpy as np
from pyhpp.core import TrapezoidalTimeParameterization
# After solving:
# path = planner.solve()
optimizer = TrapezoidalTimeParameterization(problem)
optimizer.maxVelocity = 0.5
optimizer.maxAcceleration = 0.5
p_timed = optimizer.optimize(path)
n_samples = 50
configs = []
times = []
for i in range(n_samples + 1):
t = (i / n_samples) * p_timed.length()
q, success = p_timed(t)
if success:
configs.append(np.array(q))
times.append(t)
# configs: List[np.ndarray] - configuration vectors at each sample
# times: List[float] - timestamps in secondsThe HPP configuration vector typically includes all robot DOFs. Use joint_indices to select which joints to send to ros2_control (e.g., arm joints only, excluding fingers).
from hpp_exec import send_trajectory
send_trajectory(
configs, times,
joint_names=["joint1", "joint2", ...], # ROS2 joint names
joint_indices=list(range(7)), # Which HPP config indices to use
)cd hpp-exec
./run.sh
# First time inside container:
cd ~/devel/src && make allfrom hpp_exec import (
Segment,
execute_segments,
print_segments,
send_trajectory,
segments_from_graph,
)
# Main function - send trajectory to ros2_control
send_trajectory(
configs, # List[np.ndarray] from HPP
times, # List[float] timestamps in seconds
joint_names, # List[str] ROS2 joint names
controller_topic="...", # FollowJointTrajectory action topic
)
# Expose the HPP graph segments.
configs, times, segments = segments_from_graph(path, graph)
print_segments(segments)
# Add actions exactly where this manipulation problem needs them.
segments[1].pre_actions.append(close_gripper)
segments[3].pre_actions.append(open_gripper)
execute_segments(
segments,
configs,
times,
joint_names,
)See the generated Doxygen documentation for send_trajectory_async(),
configs_to_joint_trajectory(), and other lower-level helpers.
The maintained end-to-end examples live in hpp_tutorial.
cd ~/devel/src/hpp_tutorial/tutorial_6
python -i init.pySee tutorial 6 for simple arm execution and tutorial 7 for pick-and-place with graph segments.
hpp-exec/
|-- hpp_exec/ # Python package
| |-- __init__.py
| |-- segments.py # Segment data structure
| |-- trajectory_utils.py # HPP config to ROS2 JointTrajectory conversion
| |-- ros2_sender.py # send_trajectory() via FollowJointTrajectory action
| `-- graph_segments.py # Graph segment extraction and display
|-- scripts/ # Launch scripts for Gazebo
|-- robots/ # URDF/SRDF assets
|-- docker/
|-- Dockerfile
`-- run.sh
BSD-2-Clause