216 lines
10 KiB
Plaintext
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()
|
|
```
|