Skip to content

Commit b9cc55d

Browse files
committed
feat(pin ik): nullspace and joint limits
1 parent 2680bc9 commit b9cc55d

4 files changed

Lines changed: 56 additions & 7 deletions

File tree

‎include/rcs/Kinematics.h‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,17 @@ class Pin : public Kinematics {
3838
pinocchio::Model model;
3939
pinocchio::Data data;
4040

41+
VectorXd q_lower;
42+
VectorXd q_upper;
43+
bool enforce_limits;
44+
45+
VectorXd nullspace_q;
46+
double nullspace_gain;
47+
4148
public:
42-
Pin(const std::string& path, const std::string& frame_id, bool urdf);
49+
Pin(const std::string& path, const std::string& frame_id, bool urdf,
50+
const VectorXd& nullspace_q = VectorXd(), double nullspace_gain = 0.0,
51+
bool enforce_limits = true);
4352
std::optional<VectorXd> inverse(
4453
const Pose& pose, const VectorXd& q0,
4554
const Pose& tcp_offset = Pose::Identity()) override;

‎python/rcs/_core/common.pyi‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,15 @@ class RotVec:
315315
) -> numpy.ndarray[tuple[typing.Literal[3], typing.Literal[3]], numpy.dtype[numpy.float64]]: ...
316316

317317
class Pin(Kinematics):
318-
def __init__(self, path: str, frame_id: str = "fr3_link8", urdf: bool = False) -> None: ...
318+
def __init__(
319+
self,
320+
path: str,
321+
frame_id: str = "fr3_link8",
322+
urdf: bool = False,
323+
nullspace_q: numpy.ndarray[tuple[M], numpy.dtype[numpy.float64]] = ...,
324+
nullspace_gain: float = 0.0,
325+
enforce_limits: bool = True,
326+
) -> None: ...
319327

320328
def FrankaHandTCPOffset() -> numpy.ndarray[tuple[typing.Literal[4], typing.Literal[4]], numpy.dtype[numpy.float64]]: ...
321329
def IdentityRotMatrix() -> numpy.ndarray[tuple[typing.Literal[3], typing.Literal[3]], numpy.dtype[numpy.float64]]: ...

‎src/pybind/rcs.cpp‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,9 +378,12 @@ PYBIND11_MODULE(_core, m) {
378378

379379
py::class_<rcs::common::Pin, rcs::common::Kinematics,
380380
std::shared_ptr<rcs::common::Pin>>(common, "Pin")
381-
.def(py::init<const std::string&, const std::string&, bool>(),
381+
.def(py::init<const std::string&, const std::string&, bool,
382+
const rcs::common::VectorXd&, double, bool>(),
382383
py::arg("path"), py::arg("frame_id") = "fr3_link8",
383-
py::arg("urdf") = false);
384+
py::arg("urdf") = false,
385+
py::arg("nullspace_q") = rcs::common::VectorXd(),
386+
py::arg("nullspace_gain") = 0.0, py::arg("enforce_limits") = true);
384387

385388
bind_type_class<rcs::common::RobotType>(common, "RobotType")
386389
.def_readonly_static("FR3", &rcs::common::RobotType::FR3)

‎src/rcs/Kinematics.cpp‎

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
namespace rcs {
1111
namespace common {
1212

13-
Pin::Pin(const std::string& path, const std::string& frame_id,
14-
bool urdf = false)
13+
Pin::Pin(const std::string& path, const std::string& frame_id, bool urdf,
14+
const VectorXd& nullspace_q, double nullspace_gain,
15+
bool enforce_limits)
1516
: model() {
1617
if (urdf) {
1718
pinocchio::urdf::buildModel(path, this->model);
@@ -24,6 +25,16 @@ Pin::Pin(const std::string& path, const std::string& frame_id,
2425
throw std::runtime_error(
2526
frame_id + " frame id could not be found in the provided URDF");
2627
}
28+
29+
this->q_lower = this->model.lowerPositionLimit;
30+
this->q_upper = this->model.upperPositionLimit;
31+
this->enforce_limits = enforce_limits;
32+
33+
this->nullspace_gain = nullspace_gain;
34+
this->nullspace_q = VectorXd::Zero(this->model.nq);
35+
const Eigen::Index n =
36+
std::min<Eigen::Index>(nullspace_q.size(), this->nullspace_q.size());
37+
this->nullspace_q.head(n) = nullspace_q.head(n);
2738
}
2839

2940
std::optional<VectorXd> Pin::inverse(const Pose& pose, const VectorXd& q0,
@@ -58,8 +69,26 @@ std::optional<VectorXd> Pin::inverse(const Pose& pose, const VectorXd& q0,
5869
pinocchio::Data::Matrix6 JJt;
5970
JJt.noalias() = J * J.transpose();
6071
JJt.diagonal().array() += this->damp;
61-
v.noalias() = -J.transpose() * JJt.ldlt().solve(err);
72+
73+
if (this->nullspace_gain > 0.0) {
74+
Eigen::MatrixXd Jpinv =
75+
J.transpose() *
76+
JJt.ldlt().solve(pinocchio::Data::Matrix6::Identity());
77+
v.noalias() = -Jpinv * err;
78+
79+
VectorXd dq_ns(model.nv);
80+
pinocchio::difference(model, q, this->nullspace_q, dq_ns);
81+
const Eigen::MatrixXd N =
82+
Eigen::MatrixXd::Identity(model.nv, model.nv) - Jpinv * J;
83+
v.noalias() += N * (this->nullspace_gain * dq_ns);
84+
} else {
85+
v.noalias() = -J.transpose() * JJt.ldlt().solve(err);
86+
}
87+
6288
q = pinocchio::integrate(model, q, v * this->DT);
89+
if (this->enforce_limits) {
90+
q = q.cwiseMax(this->q_lower).cwiseMin(this->q_upper);
91+
}
6392
}
6493
if (success) {
6594
return q;

0 commit comments

Comments
 (0)