Repository navigation
feat: add mount direction, per-channel calibration and mount templates - #7
Merged
Merged
Conversation
The calibrated limits now carry the mounting direction: whichever is numerically larger is the closed side, and every mm/force conversion derives its sign from that ordering (GripperConfig::close_sign()). A reverse mount has the ordering flipped, so direction stays data rather than a second field that could disagree with the limits. - Fix the shipped placeholder limits: kPosClosedRad/kPosOpenRad were swapped relative to the Python SDK (0.0/1.14 instead of 1.14/0.0), which made the default-config mm readings negative. Nothing exercised them because every test and deployment loads a calibration file. - GripperConfig gains `calibrated` (false until a calibration or a template is loaded) and `mount()` -- which reports nothing while uncalibrated, because the placeholder ordering is also close-above- open and claiming "normal" before any measurement would be a claim, not a reading. - home() targets the configured closed limit instead of the constant, so a reverse-mounted unit homes to its own closed end. - Calibration is per channel: ~/.litegrip/<channel>_calibration.json first, then the legacy single-file location, then the factory file. The automatic load skips a candidate that declares a different channel -- every LiteGrip ships at CAN id 0x08, so the channel is the only identity key on a two-gripper machine. An explicit path is the caller's override and still loads (with a warning). - Mount templates (calibration/normal.json, calibration/reverse.json) declare a direction by name via load_template(); an unknown name throws, and an unreadable template never falls back to the factory file -- that fallback is a normal mount, the one silent answer the template name exists to prevent. Templates carry no channel, ids or gains, so loading one cannot rewrite what the caller set. - save_calibration() without a path writes the per-channel file (the one the chain reads first), stamps `calibrated: true`, and omits an unknown mst_id instead of writing the falsy 0 that would pin auto-detection to RX filter 0x000. - disable_on_disconnect (default true): disconnect() can leave the motor enabled and holding, mirroring the Python attribute. - calibrate()/calibrate_guided()/calibrate_manual() mark the config calibrated; a measured run is what the flag claims. Normal-mount behaviour is bit-for-bit unchanged: close_sign() = +1 makes the extra multiply exact, and the tests assert equality rather than tolerance for the mm and force conversions. Tests: template contents and strictness, the calibrated flag, the channel-skip chain (a can1 unit must adopt neither can0's legacy file nor can0's factory file), per-channel save, reverse mm/force signs and the bit-identical normal-mount arithmetic.
|
🎉 This PR is included in version 0.2.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stage 1 of bringing this SDK up to the online litegrip-python feature set: mount direction (normal / reverse), per-channel calibration, and mount templates. Ported from the Python SDK's
GripperConfig.close_sign/mount,load_template(),default_calib_path()and its per-channel load chain.What changed
GripperConfig::close_sign()is derived (the numerically larger limit is the closed side) and every mm/force conversion uses it;mount()reads the direction back. No separate field, so it cannot disagree with the limits.calibratedflag. False until a calibration or a template loads.mount()reports nothing while it is false — the placeholder ordering is also close-above-open, so "normal" before a measurement is a claim, not a reading. (Rawgoto_rad/move_tostay ungated, the same split as Python where onlylimit_target/press_targetcheck the flag.)~/.litegrip/<channel>_calibration.json, then the legacy single-file location, then the factory file. The automatic chain skips candidates declaring a different channel (every gripper ships at CAN id 0x08 — the channel is the only identity key). An explicit path is the caller's override and loads with a warning instead.calibration/normal.json/reverse.jsonplusLiteGrip::load_template(name). Strict by design: an unknown name throws, and an unreadable template throws — never a fallback to the factory file, which is a normal mount. Templates carry no channel, ids or gains (pinned by test).save_calibration()without a path writes the per-channel file, stampscalibrated: true, and omits an unknownmst_id(a falsy 0 would pin the RX filter to 0x000).disable_on_disconnect(default true) mirrors the Python attribute;GripperBus::disconnect(bool disable = true)gains the corresponding parameter.home()targets the configured closed limit rather than the constant, so a reverse mount homes to its own closed end.calibrate*()routines mark the config calibrated.Two things worth a reviewer's attention
kPosClosedRad/kPosOpenRadwere swapped relative to the Python SDK (0.0 / 1.14 instead of 1.14 / 0.0) and against their own comment, which made default-config mm readings negative. Nothing exercised them because every test and deployment loads a calibration file. Corrected to the Python values.close_sign()is +1 there, making the extra multiply exact; the tests assert equality rather than a tolerance for the mm and force conversions.litegrip_ros2_control/litegrip_moveit_config, which consumeposition_mm, see no change for the normal mount.Verification
ctest --test-dir build --output-on-failure— 12/12, with new coverage for template contents and strictness, the calibrated flag, the channel-skip chain (a can1 unit must adopt neither can0's legacy file nor can0's factory file), per-channel save, reverse mm/force signs, and the bit-identical normal-mount arithmetic./tmpafter deleting the fixture directories — the check that caught the/tmpleak in feat: add the LiteGrip C++ SDK #5.markdownlint-cli2@0.23.3 "README.md"locally: 0 issues.This is stage 1 of the multi-stage parity plan; the motion engine / force control stage follows in a separate PR.