Repository navigation
feat(primitive): add message and service definitions for gripper capabilities - #289
Conversation
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
|
Thanks, the contract semantics are clear (dispatch vs. completion, observing
ReferencesHow common arm stacks expose the gripper (separate commands even when it shares the arm's controller, bus or SDK):
|
Grippers need stop control and contact-aware commands separate from arm joint positioning. Make supported grip request forms explicit so providers can accept a target opening width, adaptive closing, or both. Add a stop contract and a standard Time stamp for newly acquired state observations. Define the open endpoint through known width limits and centralize relative-setting validation and per-command defaults. Validation passed for protobuf, ROS 2 and MCP interface generation, protobuf compilation, all 47 generated MCP modules, and the contract/IDL comment consistency unittest. A two-process gRPC smoke test passed for info, optional-width grip, stop and timestamped state. No physical device validation or colcon build was performed. BREAKING CHANGE: Route gripper commands through gripper/*, not arm/joint_command. Regenerate gripper bindings for the updated request and state schemas. Assisted-by: Claude Code:gpt-6.1-sol
The grip width is an expected object size, not a jaw-position target. Rename the request fields and matching info flags to make that meaning explicit, and document both states of the presence flag. Keep positioning and measured-opening field names unchanged. A width match does not establish contact or task success; providers use their configured gripping tolerances. Assisted-by: Claude Code:gpt-6.1-sol
|
Thanks for the review. I've updated the contracts:
I also centralized the shared relative-parameter rules in |
HeartLinked
left a comment
There was a problem hiding this comment.
I'm writing a provider for a Jodell RG75-300 against these contracts. It is a servo parallel gripper on a separate RS-485 bus (Modbus RTU through a USB adapter on the host), next to an AUBO arm, so it has its own package and does not share the arm SDK. Everything maps cleanly except two points below.
- GripperState cannot tell a finished empty grip from one still closing.
After grip without an expected width, the caller waits for the jaws to stop. On an empty grip grasp_state is NOT_HOLDING the whole time, so the only way to see the end is to watch opening_width_m stop changing, which is the inference GraspState.msg rules out for contact. The RG75 reports this directly: its status register has a go-to bit (gGTO, 1 while moving to the target), and its object-detection field reads 0 while the fingers are still moving. control_msgs/action/ParallelGripperCommand carries stalled and reached_goal for the same reason.
Suggestion: add to GripperState a device-reported motion state with the same validity rule as grasp_state, e.g. uint8 motion_state with UNKNOWN=0, MOVING=1, STOPPED=2. A provider without such feedback reports UNKNOWN.
- Soma and the docs still read the gripper from arm/joint_states.
This PR removes the gripper from the arm contract descriptions, but Soma's gripper state still comes only from that stream (system/soma/src/store.rs collect_grippers, system/soma/src/runtime_monitor.rs JOINT_STATES), and so do system/soma/README.md, interface-catalog/primitive/arm.md, the gripper state section of integration-guide/vendor-onboarding.md and tutorials/existing-python-feature.md in robonix-book. A gripper on its own package, like this one, will never reach Soma. Could the PR description say whether Soma moves to gripper/state in a follow-up, and whether the opening may still appear in the arm's joint_states for TF as suggested in the review? The new joint_states description ("arm joint positions") reads as if it may not. An interface-catalog page for gripper/* would also help.
For reference, the RG75 mapping I plan to use: grip writes position 0xFF with force and speed scaled from relative_effort and relative_speed; object-detection 2 (stopped on an object while closing) or 1 (while opening) maps to HOLDING, 3 (reached the target with no object) to NOT_HOLDING, and 0 or a failed read to UNKNOWN. The manual warns that very thin objects may not register, so NOT_HOLDING from this device is not proof the jaws are empty. The device's own activation sweep (a full open/close at power-up) goes in the driver's activate step, not in a contract. I'll report back after it runs on the hardware.
Contact and width observations cannot reliably distinguish moving jaws from a stopped empty grip. Add an independent device-reported motion observation with UNKNOWN, MOVING and STOPPED values. Append motion_state without changing existing protobuf field numbers. Keep it separate from contact and command outcomes, use UNKNOWN for unavailable readings, and retain the shared acquisition timestamp. Protobuf, ROS 2 and MCP generation, protobuf/MCP compilation and the contract/IDL consistency test passed. A two-process gRPC test exercised motion/contact combinations. Unknown defaults and old/new protobuf payload checks passed. ROS 2 consumers need regenerated interfaces. No physical-device validation or colcon build was performed. Assisted-by: Claude Code:gpt-6.1-sol
|
Thanks for checking the contracts against a standalone gripper provider. I've added device-reported Motion and contact remain independent. You're also right about Soma, the modification of Soma and the docs documents has not been carried out yet. |
HeartLinked
left a comment
There was a problem hiding this comment.
LGTM, thanks for adding the motion state. I'll open a follow-up PR for point 2: moving Soma's gripper state to gripper/state and updating the docs.
Gripper Primitive Interface
Contract overview
robonix/primitive/gripper/inforpcgripper/srv/GetGripperInfo.srvrobonix/primitive/gripper/moverpcgripper/srv/Move.srvrobonix/primitive/gripper/griprpcgripper/srv/Grip.srvrobonix/primitive/gripper/openrpcgripper/srv/Open.srvrobonix/primitive/gripper/statetopic_outgripper/msg/GripperState.msginfo:
gripper/GetGripperInfo.srvRequest
No fields.
Response
okboolmessagestringok=false; metadata is meaningless in that casegrip_effort_configurableboolgripaccepts a non-defaultrelative_effortmove_speed_configurableboolmoveaccepts a non-defaultrelative_speedgrip_speed_configurableboolgripaccepts a non-defaultrelative_speedopen_speed_configurableboolopenaccepts a non-defaultrelative_speedopening_width_range_validboolmin_opening_width_mfloat64max_opening_width_mfloat64gripping_force_range_validboolmin_gripping_force_nfloat64max_gripping_force_nfloat64A valid range is finite with
0 <= min <= max. Width bounds apply to the current fingertips and configuration, not to nominal mechanical limits; force bounds are hardware capabilities, not the value corresponding torelative_effort. A*_configurableflag is also false when the corresponding operation is absent.move:
gripper/Move.srvRequest
opening_width_mfloat64relative_speedfloat640= configured default;(0,1]= device-relative speedResponse
okboolmessagestringReject non-finite, negative, or known-unreachable widths; do not silently clamp. The provider maps width to device coordinates, and the mapping need not be linear.
grip:
gripper/Grip.srvRequest
relative_effortfloat640= configured default;(0,1]= device-relative effort. Not newtons, and not a safety force limitrelative_speedfloat640= configured default;(0,1]= device-relative speedResponse
okboolmessagestringCloses adaptively, with no target opening width.
open:
gripper/Open.srvRequest
relative_speedfloat640= configured default;(0,1]= device-relative speedResponse
okboolmessagestringDrives to the configured open endpoint. It is not a stop, torque-off, sensor-zeroing, or calibration command.
state:
gripper/GripperState.msg(topic_out)opening_width_validboolopening_width_mopening_width_mfloat64grasp_stateuint8GraspStateconstant;UNKNOWNmarks the observation as untrustworthyThe three fields are independent observations and are not guaranteed to be sampled at the same instant.
GraspStateconstants (gripper/GraspState.msg)UNKNOWNNOT_HOLDINGHOLDINGSLIPPING