Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
cmake_minimum_required(VERSION 3.16)
project(litegrip_cpp VERSION 0.1.0 LANGUAGES CXX)

# litegrip_cpp — ROS-agnostic C++ SDK for the LiteGrip adaptive two-finger gripper.
# litegrip_cpp — C++ SDK for the LiteGrip adaptive two-finger gripper.
#
# Design constraints (see ../PLAN-litegrip-cpp.md):
# * C++17, zero third-party dependencies (stdlib + Linux SocketCAN only).
# * MUST NOT depend on ROS / ament / ros2_control — this library is the bottom
# layer that ros2_control links, and is usable from any plain C++ program.
# * Installable both into a ROS workspace (colcon, plain-cmake package.xml)
# and into a normal prefix (find_package + pkg-config).
# * No framework dependencies — usable from any plain C++ program.
# * Installable into a normal prefix (find_package + pkg-config).

if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(LITEGRIP_CPP_IS_TOP_LEVEL ON)
Expand Down
37 changes: 9 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
# litegrip_cpp

ROS-agnostic **C++ SDK** for the LiteGrip adaptive two-finger gripper.
**C++ SDK** for the LiteGrip adaptive two-finger gripper.

**English** · [简体中文](README_zh.md)

This is layer 1 of the litegrip stack — `litegrip_cpp` (SDK) → `litegrip_ros2_control`
(hardware interface) → `litegrip_moveit_config` (MoveIt). It speaks SocketCAN and
the Damiao DM4310 MIT protocol directly and depends on **nothing** but the C++17
standard library, `pthread`, and the Linux SocketCAN headers: no ROS, no ament,
no third-party libraries. Non-ROS implementations can link it as-is.
The bottom layer of the litegrip stack. It speaks SocketCAN and the Damiao
DM4310 MIT protocol directly and depends on **nothing** but the C++17 standard
library, `pthread`, and the Linux SocketCAN headers: no third-party libraries.
Any plain C++ program can link it as-is.

> Status: **layer 1 is complete** — `can/*`, `GripperBus`, `LiteGrip`,
> Status: **the SDK is complete** — `can/*`, `GripperBus`, `LiteGrip`,
> `json`/calibration, `SafetyGuard` and `ControlLoop` are all implemented and
> tested.

## What is **not** in this version

Per the agreed v1 scope: `grasp()`, `set_force()`, `move_at_speed*()` and the
public zero-gravity mode. `close(force_n=...)` accepts the argument, ignores it
and says so, because applying a grip force needs torque feed-forward and
verified force calibration.

## Safety wiring

The motion path (`goto_rad` / `move_to` / `open` / `close` / `home`) passes
Expand Down Expand Up @@ -80,7 +72,7 @@ motion, calibration, and the transport send/receive path.
| `GripperBus` | single-gripper bus API (init = hold) | `protocols/can_bus.py` |
| `LiteGrip` | high-level API | `gripper.py` |
| `SafetyGuard` + `SafetyLimits` | red lines, torque budget, watchdog, modes | `safety_limits.py` (core) |
| `ControlLoop` | background 200 Hz streaming + rate limit + gate | old ROS-side daemon |
| `ControlLoop` | background 200 Hz streaming + rate limit + gate | old Python-side daemon |

## Build

Expand All @@ -92,21 +84,18 @@ cmake --build build -j
ctest --test-dir build --output-on-failure
```

It is also buildable by `colcon` (declared as a plain-cmake package via
`package.xml`) so a ROS workspace can build it beside the ROS packages.

## Consume

```cmake
find_package(litegrip_cpp REQUIRED)
target_link_libraries(my_node PRIVATE litegrip_cpp::litegrip_cpp)
target_link_libraries(my_app PRIVATE litegrip_cpp::litegrip_cpp)
```

```bash
pkg-config --cflags --libs litegrip_cpp
```

## Non-ROS usage
## Minimal example

```cpp
#include <litegrip/litegrip.hpp>
Expand All @@ -133,11 +122,3 @@ These are the safety argument and must not be relaxed:
be bounded ⇒ do not move that way.
4. **Strict numeric boundary** — NaN / ±inf / non-numbers are rejected before any
comparison.

## Red lines are not yet unit-specific

The packaged safety baseline carries the reference unit's hand-push measurement.
They must be re-derived per gripper (caliper + closed-end re-zero) before
real-hardware motion, and `ControlLoopConfig::max_feedback_velocity_rad_s` must
be calibrated first — until it is, the loop refuses to send any motion frame
(deliberate fail-closed).
40 changes: 8 additions & 32 deletions README_zh.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# litegrip_cpp

LiteGrip 自适应两指夹爪的 **ROS 无关 C++ SDK**。
LiteGrip 自适应两指夹爪的 **C++ SDK**。

本包是 litegrip 栈的第 1 层 —— `litegrip_cpp`(SDK)→ `litegrip_ros2_control`
(硬件接口)→ `litegrip_moveit_config`(MoveIt)。它直接讲 SocketCAN 与达妙
DM4310 的 MIT 协议,依赖**只有** C++17 标准库、`pthread` 和 Linux SocketCAN
头文件:没有 ROS、没有 ament、没有第三方库。非 ROS 实现可以直接链接使用。
litegrip 栈的最底层。它直接讲 SocketCAN 与达妙 DM4310 的 MIT 协议,依赖
**只有** C++17 标准库、`pthread` 和 Linux SocketCAN 头文件:没有第三方库。
任何普通 C++ 程序都可以直接链接。

> 状态:**第 1 层已全部实现**(`can/*`、`GripperBus`、`LiteGrip`、
> 状态:**SDK 已全部实现**(`can/*`、`GripperBus`、`LiteGrip`、
> `json`/标定、`SafetyGuard`、`ControlLoop`)。

## 分层
Expand All @@ -21,7 +20,7 @@ DM4310 的 MIT 协议,依赖**只有** C++17 标准库、`pthread` 和 Linux S
| `GripperBus` | 单爪总线 API(`init` = 持位) | `protocols/can_bus.py` |
| `LiteGrip` | 高层 API | `gripper.py` |
| `SafetyGuard` + `SafetyLimits` | 红线、力矩预算、看门狗、模式 | `safety_limits.py`(核心) |
| `ControlLoop` | 后台 200 Hz 流式发送 + 限速 + 闸门 | 旧的 ROS 侧守护进程 |
| `ControlLoop` | 后台 200 Hz 流式发送 + 限速 + 闸门 | 旧的 Python 侧守护进程 |

## 构建

Expand All @@ -33,21 +32,18 @@ cmake --build build -j
ctest --test-dir build --output-on-failure
```

它同时可以被 `colcon` 构建(通过 `package.xml` 声明为 plain-cmake 包),
因此 ROS 工作空间能把它和其余 ROS 包一起构建。

## 消费方式

```cmake
find_package(litegrip_cpp REQUIRED)
target_link_libraries(my_node PRIVATE litegrip_cpp::litegrip_cpp)
target_link_libraries(my_app PRIVATE litegrip_cpp::litegrip_cpp)
```

```bash
pkg-config --cflags --libs litegrip_cpp
```

## 非 ROS 用法
## 最小示例

```cpp
#include <litegrip/litegrip.hpp>
Expand All @@ -63,12 +59,6 @@ int main() {
}
```

## 本版本**不包含**

按已确认的 v1 范围:`grasp()`、`set_force()`、`move_at_speed*()`,以及公开的零重力
模式。`close(force_n=...)` 接受该参数但**忽略并明确告警**,因为施加夹持力需要力矩
前馈与经过验证的力标定,两者都不在 v1 范围内。

## 安全不变量

这些是安全论证本身,**不得放宽**:
Expand All @@ -90,16 +80,6 @@ int main() {
- **标定流程** —— 它们本来就要把机构顶到**机械**端点,而机械端点在红线之外。
(标定与红线之间的正确关系仍是一个待定事项,见方案中的未决项。)

## 红线尚未按本台夹爪重建

随包的安全基线携带的是**参考台**的手推实测值,且文件内已明确标注
**未在本机验证**。在真机运动之前必须重新标定(卡尺 + 闭合端重设零点)并以实测值
重建红线;同时 `ControlLoopConfig::max_feedback_velocity_rad_s` 必须先行标定 ——
在该值给出之前,控制环**拒绝发送任何运动帧**(这是刻意的 fail-closed)。

推论:若某台夹爪的标定使闭合端落在红线之外,本 SDK 会(正确地)**拒绝一切运动**,
直到红线被重建。

## 测试

```bash
Expand All @@ -125,7 +105,3 @@ ctest --test-dir build --output-on-failure
覆盖** —— 需要 vcan 接口(需 root)或真机。

同样没有覆盖、需要真机的部分:连接、`init`/使能、运动、标定。

## 许可证

MIT。
2 changes: 1 addition & 1 deletion cmake/litegrip_cpp.pc.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
datadir=${prefix}/@CMAKE_INSTALL_DATADIR@

Name: litegrip_cpp
Description: ROS-agnostic C++ SDK for the LiteGrip adaptive two-finger gripper (SocketCAN / Damiao DM4310 MIT protocol)
Description: C++ SDK for the LiteGrip adaptive two-finger gripper (SocketCAN / Damiao DM4310 MIT protocol)
Version: @PROJECT_VERSION@
Libs: -L${libdir} -llitegrip_cpp
Libs.private: -lpthread
Expand Down
5 changes: 3 additions & 2 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Examples are plain C++ programs that link the SDK. They are the
# "non-ROS consumer" proof: nothing here may include ROS headers.
# Examples are plain C++ programs that link the SDK. They are the "plain C++
# consumer" proof: nothing here may include anything beyond the SDK and the
# standard library.
#
# ⚠ Examples that open can0 and drive the motor are added in T2–T4; the T1 set
# is limited to things that cannot move hardware.
Expand Down
4 changes: 2 additions & 2 deletions include/litegrip/can/controller.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// litegrip/can/controller.hpp — manages DM motors over one CAN transport.
//
// Port of litegrip_driver/litegrip/can/controller.py. Kept multi-motor capable
// even though LiteGrip uses a single motor: it is the reusable "outside ROS"
// surface for anyone driving several Damiao motors on one bus.
// even though LiteGrip uses a single motor: it is the general-purpose surface
// for anyone driving several Damiao motors on one bus.

#pragma once

Expand Down
10 changes: 5 additions & 5 deletions include/litegrip/control_loop.hpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// litegrip/control_loop.hpp — background streaming control loop.
//
// Replaces the old ros2_control Python daemon (hw_daemon.py + sdk_adapter.py +
// the shared-memory bridge). With a C++ SDK the two-process split has no reason
// to exist: the ros2_control hardware component links this class directly.
// Replaces the old Python daemon (hw_daemon.py + sdk_adapter.py + the
// shared-memory bridge). With a C++ SDK the two-process split has no reason to
// exist: the layer above links this class directly.
//
// R1: the loop runs on its own background thread. A DM motor needs a continuous
// MIT frame stream (~900 ms of silence latches the 0xD comm-loss fault), and
// the controller_manager cycle is not guaranteed stable — so the plugin's
// write() only posts a target and its read() only reads a cached snapshot.
// the caller's cycle is not guaranteed stable — so the layer above only posts a
// target and reads a cached snapshot.
//
// What the loop owns (all of it used to be spread across safety_gate.py,
// driver_adapter.py's TrajectoryLimiter and sdk_adapter.py's _send_motion):
Expand Down
5 changes: 2 additions & 3 deletions include/litegrip/gripper.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// litegrip/gripper.hpp — high-level gripper API (LiteGrip).
//
// Port of litegrip_driver/litegrip/gripper.py. This is the entry point most
// non-ROS consumers use: connect, init (hold), open/close/goto, calibrate,
// read state.
// consumers use: connect, init (hold), open/close/goto, calibrate, read state.
//
// v1 capability scope (PLAN-litegrip-cpp.md D6). Deliberately NOT in v1:
// * grasp() / set_force() — force control, deferred
Expand Down Expand Up @@ -43,7 +42,7 @@ class LiteGrip {
LiteGrip& operator=(LiteGrip&& other) noexcept;

/// Connect and return a live instance (throws ConnectError on failure).
/// The RAII one-liner for the non-ROS case.
/// The RAII one-liner: the returned object disconnects on destruction.
static LiteGrip connect_raii(GripperConfig config = GripperConfig{});

// ── properties ────────────────────────────────────────────────────────
Expand Down
8 changes: 4 additions & 4 deletions include/litegrip/safety.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
//
// Ported from the safety-aware SDK variant's safety_limits.py (core only —
// exclude the contact/force/stall/no-load physics models, see
// PLAN-litegrip-cpp.md D5), plus the ROS-side gate's two hard ceilings
// PLAN-litegrip-cpp.md D5), plus the old Python gate's two hard ceilings
// (TORQUE_LIMIT_CEILING_NM / MAX_COMMAND_VELOCITY_CEILING_RAD_S) so that the
// whole stack keeps a single source of truth now that the ROS-side Python is
// going away (D2/D4).
// whole stack keeps a single source of truth now that the old Python is going
// away (D2/D4).
//
// The Python original's long-form Chinese rationale is NOT copied here — it
// lives in safety_limits.py and in the plan. What is preserved is the set of
Expand Down Expand Up @@ -62,7 +62,7 @@ inline constexpr double kProtocolQMaxRad = 12.5;
/// protocol range / a fake 0 read from nothing.
inline constexpr double kZeroTorqueQFallback = -0.6;

/// ROS-side torque hard ceiling, N.m. Read-only: limits may only go below it.
/// Torque hard ceiling, N.m. Read-only: limits may only go below it.
inline constexpr double kTorqueLimitCeilingNm = 3.5;

/// Command-trajectory velocity hard ceiling, rad/s. Read-only.
Expand Down
15 changes: 6 additions & 9 deletions package.xml
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>litegrip_cpp</name>
<version>0.1.0</version>
<description>
ROS-agnostic C++ SDK for the LiteGrip adaptive two-finger gripper.
C++ SDK for the LiteGrip adaptive two-finger gripper.

This is the bottom layer of the litegrip stack (sdk → ros2_control →
moveit_config). It speaks SocketCAN and the Damiao DM4310 MIT protocol
directly and depends on nothing but the C++17 standard library and the
Linux SocketCAN headers — deliberately no ROS, no ament, no third-party
libraries, so that non-ROS implementations can reuse it as-is.
This is the bottom layer of the litegrip stack. It speaks SocketCAN and the
Damiao DM4310 MIT protocol directly and depends on nothing but the C++17
standard library and the Linux SocketCAN headers — no third-party
libraries.

Declared as a plain-cmake package (build_type cmake) purely so a colcon
workspace can build it alongside the ROS packages; the library itself is
Declared as a plain-cmake package (build_type cmake); the library itself is
built and installed like any ordinary CMake project.
</description>
<maintainer email="TODO@email.com">TODO</maintainer>
Expand Down
14 changes: 7 additions & 7 deletions src/control_loop.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// control_loop.cpp — the background control loop.
//
// Replaces the old ros2_control Python daemon + shared-memory bridge: with a
// C++ SDK the two-process split has no reason to exist, so "rate-limit the
// target, allocate the torque budget, gate the frame, stream it, watch the
// feedback" now lives in one thread inside the library.
// Replaces the old Python daemon + shared-memory bridge: with a C++ SDK the
// two-process split has no reason to exist, so "rate-limit the target, allocate
// the torque budget, gate the frame, stream it, watch the feedback" now lives
// in one thread inside the library.
//
// R1: the loop owns a background thread. A DM motor needs a continuous MIT
// frame stream (~900 ms of silence latches the 0xD comm-loss fault) and the
// controller_manager cycle is not guaranteed stable, so the layer above only
// posts a target and reads a cached snapshot.
// caller's cycle is not guaranteed stable, so the layer above only posts a
// target and reads a cached snapshot.
//
// dry_run is not "do nothing": it runs the whole control path — rate limiting,
// torque-budget allocation and the safety gate — against a simulated plant and
// only skips opening CAN and sending. That makes the gate and the
// deploy-config fail-closed behaviour testable without hardware, and is what
// makes a dry-run ros2_control stack meaningful.
// makes a dry-run deployment meaningful.

#include "litegrip/control_loop.hpp"

Expand Down
2 changes: 1 addition & 1 deletion src/safety.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ SafetyLimits load_safety_baseline(const std::optional<std::string>& version) {
: safety_baseline_path(wanted);
if (!file_exists(path)) {
// Fail-closed: silently falling back would make "the config was lost" look
// identical to "the config is correct", and the node would run with a
// identical to "the config is correct", and the process would run with a
// torque ceiling nobody confirmed.
throw SafetyConfigError("safety baseline '" + wanted +
"' file does not exist: " + path +
Expand Down
2 changes: 1 addition & 1 deletion src/version.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// litegrip_cpp — ROS-agnostic C++ SDK for the LiteGrip adaptive two-finger gripper.
// litegrip_cpp — C++ SDK for the LiteGrip adaptive two-finger gripper.

#include "litegrip/version.hpp"

Expand Down
Loading