Files
2026-10-16 17:27:30 +08:00

216 lines
10 KiB
Plaintext

# 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()
```