Subgroup Planning¶
The X500 exposes 6 joints: the body (x, y, z, yaw) — virtual joints,
since the flight controller holds roll and pitch — and the gimbal
(pan, tilt). A subgroup is an ordered list of indices into this 6-DOF
configuration: the planner reasons in the reduced joint space while the
C++ collision pipeline injects the frozen stance for the other joints and
still checks the full body.
Built-in aliases¶
| Alias | DOF | What it controls |
|---|---|---|
x500_base |
4 | body (x, y, z, yaw); the gimbal holds its pose |
x500_whole_body |
6 | body + gimbal pan, tilt |
x500 |
6 | alias for x500_whole_body |
The list is a dict in drone_planning.x500.PLANNING_SUBGROUPS, also
listed by
available_robots().
Using a built-in alias¶
from drone_planning.planning import create_planner
from drone_planning.types import PlannerConfig
live_config = drone.get_state() # 6-D array from your system
planner = create_planner(
"x500_base",
config=PlannerConfig(time_limit=1.0),
base_config=live_config, # the gimbal is held here
pointcloud=obstacle_cloud,
)
start = planner.extract_config(live_config) # 6-D → 4-D
result = planner.plan(start, goal_body)
full_path = planner.embed_path(result.path) # 4-D path → 6-D path
base_config defaults to HOME_JOINTS (hovering 1 m above the origin,
gimbal level and forward). Pass the live state instead to pin the
gimbal where it is — collision checks see the real stance.
set_subgroup(robot_name, base_config) switches an existing planner to
another subgroup without rebuilding the collision environment; the
point cloud and the joint limits (set_joint_limits) are kept.
Low-level subgroups¶
Any ordered list of joint indices is a valid subgroup with the C++ binding:
import numpy as np
from drone_planning._ompl_vamp import OmplVampPlanner
from drone_planning.x500 import HOME_JOINTS
planner = OmplVampPlanner([2, 3], HOME_JOINTS.tolist()) # altitude + yaw only
planner.add_pointcloud(cloud, 0.0) # (N, 3) float32
result = planner.plan(np.array([1.0, 0.0]), np.array([2.0, 1.5]),
planner_name="rrtc", time_limit=1.0)
The binding takes numpy arrays (float64 configurations, float32 points).
set_subgroup(active_indices, frozen_config) switches it in place.
When it matters¶
The gimbal never collides with the body (see the SRDF in
Robot Model), but it hangs 14–18 cm below it and can touch
obstacles: plan with x500_whole_body when the camera has to point
somewhere on arrival, and with x500_base (a 4-D search) when the gimbal
can stay where it is.