Solver kernel#
The batched HJCD solver: block-cooperative coarse search, then warp-per-candidate Levenberg–Marquardt refinement.
The native host API lives in csrc/kernel/hjcd_kernel.h. Both sampling and solving
serialize access to shared CUDA constants and stop flags. Python releases the GIL while
these operations run. Models and joint limits are cached per CUDA device; callers must
keep the CUDA context alive (calling cudaDeviceReset invalidates those caches).
Result<T> owns its host arrays, releases them on destruction, and supports moves
but not copies. Temporary device allocations are released on normal return and exceptions.
HJCD CUDA runtime failures raise std::runtime_error (Python RuntimeError),
including generated model/limit initialization. HJCD uses GRiD’s checked APIs:
initialization failures report the operation, roll back partial allocations best-effort,
and never reset the CUDA context or terminate the process. This does not promise
recovery after a context-invalidating CUDA error.
A null model argument selects the internally cached model for sampling. The solver always uses its own internally cached coarse/refine models (one per precision) and takes no model argument. Position errors are in millimeters and orientation errors in radians.
Enums
-
enum class CollisionMode : int#
Collision policy of one solve.
noneis an open-world solve.hardfilters self and environment collisions against the compiled sphere model;softranks candidates by environment penetration only (no self-collision check, no filtering) and requires a non-empty world;bothranks and filters.Values:
-
enumerator none#
-
enumerator hard#
-
enumerator soft#
-
enumerator both#
-
enumerator none#
Functions
-
std::shared_ptr<CollisionWorld> make_collision_world(const WorldSpec &spec)#
Validate and upload a world to the calling thread’s current CUDA device.
Requires a collision-enabled build. Values must be finite and within fp32 range; radii and side lengths positive; quaternions non-zero (they are normalised). The returned object frees its device memory on destruction, on the device it was made on, and may be shared between solves.
- Throws:
std::invalid_argument – For invalid geometry.
std::runtime_error – Without collision geometry in the build, or on CUDA failures.
-
int collision_world_device(const CollisionWorld &world)#
CUDA device the world lives on.
-
bool collision_world_empty(const CollisionWorld &world)#
True when the world holds no primitive.
-
std::array<int, 3> collision_world_counts(const CollisionWorld &world)#
Primitive counts: spheres, capsules, boxes.
-
template<typename T, typename RT = double>
Result<T> generate_ik_solutions(const T *target_pose, int b_size, int num_solutions = 1, CollisionMode collision = CollisionMode::none, const CollisionWorld *world = nullptr, bool collect_stats = false, const SolveOptions &options = SolveOptions{})# Solve one end-effector target with a GPU batch of candidate configurations.
Compiled instantiations are <double,double>, <double,float>, and <float,double>. T controls I/O; RT controls LM compute precision; coarse search always uses float. Calls serialize shared native state on the calling thread’s current CUDA device and use internally cached, precision-specific generated models. Cached models require that context to remain alive; cudaDeviceReset is unsupported.
- Parameters:
target_pose – Nonnull seven-vector [x,y,z,qw,qx,qy,qz], meters and scalar-first quaternion. Finite nonzero quaternions are normalized without mutating this input.
b_size – Positive candidate count (not a target count), bounded by CUDA indexing.
num_solutions – Positive maximum number of distinct candidates requested.
collision – Collision policy; anything but none requires a collision-enabled build.
world – Uploaded obstacle world on the current device, or nullptr for an empty environment. hard/both with an empty world check self-collision only; soft with an empty world is rejected.
collect_stats – Fill Result::stats (sets Result::has_stats). Never changes the solution.
options – Margin, self-collision toggle, seed and warm starts; see SolveOptions.
- Throws:
std::invalid_argument – For invalid scalar/pose values, a soft solve without obstacles, or a world made on another device.
std::runtime_error – For checked CUDA failures or a collision request on an open-world build. Generated model/limit initialization uses GRiD’s checked APIs and raises on failure.
- Returns:
Owning result. Candidates may be approximate, fewer than requested, or empty. Collision checks describe configurations, never paths.
-
template<typename T>
std::vector<std::array<T, 7>> sample_random_target_poses(int num_configs, std::uint64_t seed)# Sample reachable world-frame poses from a seeded Halton joint sequence.
T may be float or double. The result uses [x,y,z,qw,qx,qy,qz], meters and scalar-first quaternions. Sampling respects joint limits but does not check collision. Calls share the solver lock and use the calling thread’s current CUDA device.
- Parameters:
num_configs – Positive target count bounded by CUDA launch indexing.
seed – Unsigned seed used to shift the Halton sequence.
-
void init_joint_limits_from_grid()#
Lazily initialize generated joint limits on the current device; normally called internally.
-
int grid_num_joints()#
Return the compiled actuated joint count without initializing CUDA.
-
bool grid_has_collision()#
Return whether the compiled header includes collision geometry, without initializing CUDA.
-
struct SolveStats#
- #include <hjcd_kernel.h>
Per-solve diagnostics, collected only when requested.
Unmeasured fields stay -1.
Accuracy tallies use position error < 5 mm and orientation error < 1e-3 rad. In hard/both mode the collision tallies include self and environment checks; in soft mode they count environment penetration only. env_cost_* are refined-candidate penetration summaries in soft/both mode.
Public Members
-
int batch_size = -1#
Coarse candidates (B).
-
int n_refined = -1#
LM-refined candidates (K x repeats).
-
int n_ik_accurate = -1#
Refined candidates inside the accuracy band.
-
int n_coll_free_refined = -1#
Refined candidates passing the selected collision check.
-
int n_feasible = -1#
Accurate and collision-free refined candidates.
-
int n_ik_lost = -1#
Accurate refined candidates rejected by the collision check.
-
int n_coll_in_refined = -1#
Refined candidates rejected by the collision check.
-
int n_coll_in_coarse = -1#
Coarse candidates rejected by the collision check.
-
double env_cost_min_mm = -1#
Minimum refined environment penetration cost (soft/both).
-
double env_cost_max_mm = -1#
Maximum refined environment penetration cost (soft/both).
-
double env_cost_mean_mm = -1#
Mean refined environment penetration cost (soft/both).
-
int n_returned = -1#
Returned candidates.
-
int n_returned_ik_accurate = -1#
Returned candidates inside the accuracy band.
-
int n_returned_coll_free = -1#
Returned candidates passing the selected collision check.
-
double pct_returned_coll_free = -1#
100 * n_returned_coll_free / n_returned (0 when none returned).
-
int n_returned_feasible = -1#
Returned candidates accurate and collision-free.
-
int batch_size = -1#
-
template<typename T>
struct Result# - #include <hjcd_kernel.h>
Move-only owner of a native solve’s row-major host buffers.
Read only the first count rows. A returned candidate is not a success certificate: check both error arrays against your tolerances. Collision filtering can yield count == 0. Buffers remain valid until this object is reset, destroyed, or moved from. Do not delete the individual pointers; ownership follows the Result object.
Public Functions
-
Result() = default#
-
inline ~Result()#
-
inline void reset() noexcept#
Release all owned buffers and restore the empty state; safe to call repeatedly.
Public Members
-
int count = 0#
Number of returned candidates, possibly less than requested.
-
bool has_stats = false#
True when stats was collected for this solve.
-
SolveStats stats = {}#
Diagnostics; meaningful only when has_stats.
-
Result() = default#
-
struct WorldSpec#
- #include <hjcd_kernel.h>
Host description of an obstacle world in the robot base frame, metres, scalar-first quaternions.
Row-major flat arrays: spheres rows are [cx cy cz r]; capsules rows are [x0 y0 z0 x1 y1 z1 r] (segment endpoints and radius); boxes rows are [cx cy cz qw qx qy qz sx sy sz] with full side lengths. Cylinders are not a native primitive: callers convert them to capsules first.
-
struct SolveOptions#
- #include <hjcd_kernel.h>
Per-solve knobs beyond the collision policy.
Defaults reproduce the historical solver.
margin_mis the clearance (metres) below which an environment pair counts as a hit; self pairs retain zero margin whencheck_selfis true. Zero margin is today’s contact-free rule. Both apply tohardandbothonly (invalid otherwise).seedshifts the coarse search’s random initial configurations (it changes which valid candidates are found, never their validity).q_init(row-majornum_initx N, radians/metres, clamped to joint limits on the device) replaces the initial configurations of the firstnum_initcoarse blocks;num_initmust not exceed the batch size.
-
namespace grid#