controller: take stall and reached from the gripper's object status, not a velocity timeout
Problem
parallel_gripper_action_controller decides a goal from one input: the joint velocity. A goal is stalled when |velocity| stays under stall_velocity_threshold for stall_timeout, and reached when the position error is under goal_tolerance. With #92 the driver feeds a real velocity estimate, which makes this work, but the verdict comes about 1.4 s after the fingers stop: 0.5–0.7 s for the estimate to decay under the threshold, then the 1.0 s timeout. Every close-on-object pays it, and every client sees it (the MCP's first grasp verdict, MoveIt grasp checks).
The gripper already decides this itself. Its status reports object detection (Moving, DetectedWhileOpening, DetectedWhileClosing, AtRequestedPosition), the driver exports it as the object_status state interface, and the status broadcaster from #78 publishes it. The controller is the only consumer that ignores it.
Proposal
Add robotiq_controllers/GripperActionController, a copy of parallel_gripper_action_controller (the implementation is a single header) that claims object_status as an additional state interface and decides in check_for_success from it:
DetectedWhileOpening / DetectedWhileClosing → stalled = true, result position from the current reading; success or abort per allow_stalling as today.
AtRequestedPosition → reached_goal = true.
Moving → keep waiting; the velocity timeout stays as a safety net behind it.
When the joint exports no object_status (the topic-based Isaac Sim config, any GenericSystem), fall back to the velocity path unchanged, so the controller is a drop-in replacement in every configuration.
Gate it behind a use_object_status parameter, default false in the first release so behaviour matches PickNik's controller; flip the default once the bench confirms the verdict latency and the false-stall rate over the 20-cycle and close-on-block runs from #93.
Humble stays on the stock GripperCommand controller and keeps the velocity stall. Humble EOL: nothing to delete there; the fork is Jazzy and newer only.
Expected gain
Verdict latency drops from about 1.4 s to one exchange plus one controller cycle after the firmware's own stop decision. The firmware's decision time is not in the manual and needs one bench measurement before it goes in the README.
Consumers
Once the flags carry the firmware's decision, clients stop second-guessing them. The MCP's classify (mcp/gripper_mcp/service.py) currently reads the verdict from the achieved position against the target with a datasheet closed_tolerance_mm, because stalled was untrustworthy (#29) and goal_tolerance is wider than the datasheet's tolerance (#91). With use_object_status on, it should trust reached_goal and stalled and drop the position rule and closed_tolerance_mm. Same for any MoveIt grasp check that re-derives object detection from position.
Follow-up to
#92 (velocity estimate). Not a change to #92: the estimate stays, both as the fallback and for joint_states.
controller: take stall and reached from the gripper's object status, not a velocity timeout
Problem
parallel_gripper_action_controllerdecides a goal from one input: the joint velocity. A goal is stalled when|velocity|stays understall_velocity_thresholdforstall_timeout, and reached when the position error is undergoal_tolerance. With #92 the driver feeds a real velocity estimate, which makes this work, but the verdict comes about 1.4 s after the fingers stop: 0.5–0.7 s for the estimate to decay under the threshold, then the 1.0 s timeout. Every close-on-object pays it, and every client sees it (the MCP's first grasp verdict, MoveIt grasp checks).The gripper already decides this itself. Its status reports object detection (
Moving,DetectedWhileOpening,DetectedWhileClosing,AtRequestedPosition), the driver exports it as theobject_statusstate interface, and the status broadcaster from #78 publishes it. The controller is the only consumer that ignores it.Proposal
Add
robotiq_controllers/GripperActionController, a copy ofparallel_gripper_action_controller(the implementation is a single header) that claimsobject_statusas an additional state interface and decides incheck_for_successfrom it:DetectedWhileOpening/DetectedWhileClosing→stalled = true, result position from the current reading; success or abort perallow_stallingas today.AtRequestedPosition→reached_goal = true.Moving→ keep waiting; the velocity timeout stays as a safety net behind it.When the joint exports no
object_status(the topic-based Isaac Sim config, any GenericSystem), fall back to the velocity path unchanged, so the controller is a drop-in replacement in every configuration.Gate it behind a
use_object_statusparameter, defaultfalsein the first release so behaviour matches PickNik's controller; flip the default once the bench confirms the verdict latency and the false-stall rate over the 20-cycle and close-on-block runs from #93.Humble stays on the stock
GripperCommandcontroller and keeps the velocity stall.Humble EOL:nothing to delete there; the fork is Jazzy and newer only.Expected gain
Verdict latency drops from about 1.4 s to one exchange plus one controller cycle after the firmware's own stop decision. The firmware's decision time is not in the manual and needs one bench measurement before it goes in the README.
Consumers
Once the flags carry the firmware's decision, clients stop second-guessing them. The MCP's
classify(mcp/gripper_mcp/service.py) currently reads the verdict from the achieved position against the target with a datasheetclosed_tolerance_mm, becausestalledwas untrustworthy (#29) andgoal_toleranceis wider than the datasheet's tolerance (#91). Withuse_object_statuson, it should trustreached_goalandstalledand drop the position rule andclosed_tolerance_mm. Same for any MoveIt grasp check that re-derives object detection from position.Follow-up to
#92 (velocity estimate). Not a change to #92: the estimate stays, both as the fallback and for
joint_states.