# llms.txt - Guidance for generating Flexiv robot control code in this repository Scope: This file captures repository-specific, API-accurate knowledge for LLMs that generate Python control scripts using `flexivrdk`. ## 0) Authoritative References - RDK Manual: https://www.flexiv.com/software/rdk/manual - API Documentation (latest): https://www.flexiv.com/software/rdk/api - Python package page: https://pypi.org/project/flexivrdk - Primitive catalog and documentation: https://www.flexiv.com/software/primitives Use these sources for API behavior details and argument meaning when in doubt. ## 0.1) C++ and Python API Parity - In this project, Python RDK is generated from pybind bindings of C++ RDK. - Function names and function documentation are expected to match between C++ and Python APIs. - Generation rule: if Python docs are sparse, use the corresponding C++ API docs as canonical backup for names, parameters, and semantics. - Verification rule: before emitting a Python API call, verify parameter count, optionality, and fixed-size container constraints against API docs or headers. - Priority references for verification: API docs first, then C++ headers (for exact sizes/types when docs are ambiguous). ## 1) Preferred Script Pattern (Python) - Use examples under `example_py/` as style reference. - Typical imports: - `import argparse` - `import time` - `import spdlog` - `import flexivrdk` - Typical flow: 1. Parse `robot_sn` argument. 2. `robot = flexivrdk.Robot(robot_sn)`. 3. If `robot.fault()`: call `robot.ClearFault()` and handle failure. 4. `robot.ServoOn()` and wait until `robot.operational()`. 5. Get applicable joint groups from `robot.info()`: - for primitive execution, use `single_arm_groups = robot.info().single_arm_groups` - for inspection/logging of all groups, use `robot.info().all_groups` 6. If needed, use `robot.Home()` (or `robot.Home([...])`) as the blocking convenience wrapper for homing. 7. Switch to primitive execution with `robot.SwitchMode(flexivrdk.Mode.NRT_PRIMITIVE_EXECUTION)`. 8. Execute primitives with `robot.ExecutePrimitive({group: flexivrdk.PrimitiveArgs(name, params) for group in single_arm_groups})`. 9. Wait for primitive completion using the primitive's documented Default Transition Condition from each running group's primitive state map. 10. End with `robot.Stop()`. ## 2) Primitive Execution Conventions - Use explicit primitive names such as `Home`, `MoveJ`, `MoveL`. - For each primitive call, wrap parameters in `flexivrdk.PrimitiveArgs(name, params)` and send as a map keyed by joint group. - Use only existing single-arm joint groups, typically the keys of `robot.info().single_arm_groups`, with `robot.ExecutePrimitive(...)`. - Keep transitions explicit in code (sequential primitive calls) unless user asks for asynchronous behavior. - Do not assume `robot.ExecutePrimitive(...)` blocks until completion. It blocks until request delivery or primitive start; users must still wait on primitive states. - Primitive transition rule: - Each primitive exposes a set of primitive states as listed in Primitive State Parameters. - In most cases, use the primitive's documented Default Transition Condition. But other Primitive State Parameters can also be used as transition conditions. - Do not assume `reachedTarget` is the only valid transition condition for all primitives. - Polling loop pattern: - `all_states = robot.primitive_states()` - `robot.primitive_states()` only contains joint groups that are currently running primitives - check the documented state key(s) from `state.names_and_values` for each target joint group - primitive-state booleans are typically read back as integer `1` or `0` - `time.sleep(0.1-1.0)` in the loop ### 2.1) RDK v2.x Joint-Group API Rule - `robot.states()` returns `dict[JointGroup, RobotStates]`, not a single `RobotStates`. - `robot.primitive_states()` returns `dict[JointGroup, PrimitiveStates]`, not a flat key-value map. - `robot.ExecutePrimitive(...)` takes `dict[JointGroup, PrimitiveArgs]`, not `(name, params)`. - `robot.Home(groups=...)` is the blocking convenience wrapper for the Home primitive and accepts a list of single-arm joint groups. If omitted, it homes all applicable single-arm groups. - When using fields like `tcp_pose`, first choose a specific joint group, usually one from `robot.info().single_arm_groups`. ## 3) Data Type Mapping for `ExecutePrimitive` Map plan-like values to RDK Python objects: - `JPOS` -> `flexivrdk.JPos(q_m, q_e)` or `flexivrdk.JPos(q_m)` - `COORD` -> `flexivrdk.Coord(position, orientation, ref_frame, ref_q_m, ref_q_e)` - `ARRAY_COORD` -> Python list of `flexivrdk.Coord(...)` - Scalar types: - `BOOL` -> `bool` - `INT` -> `int` - `DOUBLE` -> `float` - `TYPE` -> `str` - `VEC_3d` -> Python list/array length 3 ### 3.1) Fixed-Size Argument Checklist (Pybind) - Treat constructor/type hints with fixed sizes as strict runtime requirements. - `flexivrdk.JPos(q_m)` requires `q_m` length 7. `flexivrdk.JPos(q_m, q_e)` requires `q_e` length 6. - `flexivrdk.Coord(position, orientation, ref_frame)` requires lengths 3, 3, and 2 respectively. - Optional `Coord` arguments still have fixed sizes when provided: `ref_q_m` length 7 and `ref_q_e` length 6. - If `ref_q_e` is provided, also provide `ref_q_m`. - Prefer omitting optional constructor arguments entirely over passing empty or wrong-sized placeholders. ## 4) Critical `Coord` Rule (Do Not Drop Provided Fields) From `include/flexiv/rdk/data.hpp`, the current `Coord` signature is: `flexivrdk.Coord(position, orientation, ref_frame, ref_q_m, ref_q_e)` Its members are, in order: 1. `position` 2. `orientation` 3. `ref_frame` 4. `ref_q_m` 5. `ref_q_e` Generation rule: - `ref_q_m` and `ref_q_e` are optional in the constructor, but if source data provides them, populate them. - Do not silently drop `ref_q_m` or `ref_q_e`. - If specifying `ref_q_e`, also specify `ref_q_m`. ## 5) Units Guidance - RDK `JPos` and `Coord` objects use SI length unit `[m]` on the API side. - Header note: conversion between RDK `[m]` and Flexiv Elements custom-type `[mm]` is handled automatically for `JPOS` and `COORD` exchanged through APIs such as `ExecutePrimitive()`. - Primitive-documentation caveat: primitive input parameters do not universally use SI units. - For scalar primitive parameters, follow the primitive page exactly. - For `flexivrdk.Coord(...)` and `flexivrdk.JPos(...)`, supply values in the RDK object units defined by the headers. ## 5.1) Upright Posture Convention - The robot upright posture is defined as all joint positions being 0. ## 5.2) Euler Readback Normalization - Robot Euler orientation readback is normalized to the range [-180, 180] degrees. - If an Euler angle increases past 180, it wraps to -180. If it decreases past -180, it wraps to 180. - This wraparound does not indicate a different physical orientation; it is only a different representation of the same rotation. - When comparing Euler angles from commanded values versus robot readback, compare with wraparound awareness instead of raw numeric equality. ## 6) Safety and Robustness Defaults - Always include fault clear path before enabling. - Always wait for operational state before switching mode or sending primitives. - Use try/except and log exceptions with `spdlog`. - Avoid busy-spin loops; include `time.sleep(...)` in polling loops. - Prefer timeout-backed wait helpers over unbounded polling loops when generating new scripts. - Keep command sequence deterministic and human-readable. ## 7) Output Style for Generated Scripts - Prefer one self-contained runnable script per task. - Include short comments only for non-obvious behavior. - Keep numeric trajectory data hardcoded only when user explicitly asks for hardcoding. - Do not parse external plan files if the user asks for hardcoded primitive calls. ## 8) Repository Anchors - Python examples: `example_py/` - Agent primitive-only reference: `example_py/AGENT_REFERENCE_primitives_only.py` - Primitive tutorial reference: `example_py/basics3_primitive_execution.py` - Primitive wait helper reference: `example_py/utility.py` - Core robot API surface: `include/flexiv/rdk/robot.hpp` - Core data type definitions: `include/flexiv/rdk/data.hpp` - Repo readme and setup guide: `README.md` ## 9) Minimal Skeleton ```python import argparse import time import spdlog import flexivrdk import utility def wait_primitive_transition(robot, transition_keys_by_group, dt=0.2, timeout_s=60.0): """Block until primitive_states() for all groups satisfy their transition key. Use the primitive's default transition key unless a custom transition condition is intended. Check primitive docs to confirm available primitive state keys. """ t0 = time.time() while True: if utility.primitive_state_true_for_groups( robot.primitive_states(), transition_keys_by_group, ): return if time.time() - t0 > timeout_s: raise TimeoutError( "Timed out waiting for primitive transition states: " f"{ {flexivrdk.kJointGroupNames[g]: k for g, k in transition_keys_by_group.items()} }" ) time.sleep(dt) def main(): parser = argparse.ArgumentParser() parser.add_argument("robot_sn") args = parser.parse_args() logger = spdlog.ConsoleLogger("LLMGenerated") try: robot = flexivrdk.Robot(args.robot_sn) if robot.fault(): logger.warn("Fault detected, clearing") if not robot.ClearFault(): raise RuntimeError("Cannot clear fault") robot.ServoOn() while not robot.operational(): time.sleep(1) single_arm_groups = robot.info().single_arm_groups if not single_arm_groups: raise RuntimeError("No single-arm joint group found on the connected robot") robot.SwitchMode(flexivrdk.Mode.NRT_PRIMITIVE_EXECUTION) robot.ExecutePrimitive( { group: flexivrdk.PrimitiveArgs("Home", {}) for group in single_arm_groups } ) wait_primitive_transition(robot, {group: "reachedTarget" for group in single_arm_groups}) # Add other primitive calls here. robot.Stop() except Exception as e: logger.error(f"Unhandled exception: {e}") raise if __name__ == "__main__": main() ```