rby1-sdk
Loading...
Searching...
No Matches
robot.h
1#pragma once
2
3#include <Eigen/Core>
4#include <iostream>
5#include <memory>
6#include <queue>
7#include <string>
8#include <type_traits>
9#include <utility>
10#include <vector>
11
12#include "rby1-sdk/dynamics/inertial.h"
13#include "rby1-sdk/dynamics/joint.h"
14#include "rby1-sdk/dynamics/link.h"
15#include "rby1-sdk/dynamics/state.h"
16#include "rby1-sdk/export.h"
17#include "rby1-sdk/math/liegroup.h"
18
19namespace rb::dyn {
20
21/**********************************************************
22 * FORWARD DECLARATION
23 **********************************************************/
24
25//
26template <int DOF>
27class Robot;
28class Link;
29class Joint;
30
31/**********************************************************
32 * MOBILE BASE
33 **********************************************************/
34
35enum class MobileBaseType {
36 kNone, //
37 kDifferential, //
38 kMecanum //
39};
40
41struct RBY1_SDK_API MobileBase {
42 MobileBaseType type{MobileBaseType::kNone};
43
44 rb::math::SE3::MatrixType T; // front = x-axis
45 std::vector<std::string> joints;
46 std::vector<double> params;
47};
48
49struct RBY1_SDK_API MobileBaseDifferential : public MobileBase {
50 unsigned int right_wheel_idx{};
51 unsigned int left_wheel_idx{};
52
53 double wheel_base{0.};
54 double wheel_radius{0.};
55};
56
57struct RBY1_SDK_API MobileBaseMecanum : public MobileBase {
58 unsigned int fr_wheel_idx{};
59 unsigned int fl_wheel_idx{};
60 unsigned int rr_wheel_idx{};
61 unsigned int rl_wheel_idx{};
62
63 double L_x{0.};
64 double L_y{0.};
65 double wheel_radius{0.};
66};
67
68/**********************************************************
69 * ROBOT
70 **********************************************************/
71
72struct RBY1_SDK_API RobotConfiguration {
73 std::string name;
74 std::shared_ptr<Link> base_link;
75 std::shared_ptr<MobileBase> mobile_base;
76};
77
78template <int DOF>
79class Robot {
80 public:
81 template <typename T, int N>
82 using ContainerType = typename std::conditional_t<(N > 0), std::array<T, (unsigned int)N>, std::vector<T>>;
83
84 struct Link_ {
85 struct SubLink_ {
86 std::shared_ptr<Link> link;
87 Inertial::MatrixType J_wrt_p;
88 math::SE3::MatrixType M_wrt_p;
89 math::SE3::MatrixType M_wrt_base;
90 };
91
92 std::vector<SubLink_> links;
93 Inertial::MatrixType J{Inertial::MatrixType::Zero()}; // Link_ inertial wrt base link
94 math::SE3::MatrixType M{math::SE3::Identity()}; // Base link to Link_ frame
95
96 Inertial::MatrixType I{Inertial::MatrixType::Zero()}; // Link_ inertial wrt Link_ frame
97
98 int depth{0};
99 int parent_joint_idx{-1};
100 std::vector<int> child_joint_idx{};
101
102 void SetBaseLink(const std::shared_ptr<Link>& link, const math::SE3::MatrixType& T) {
103 // Rest link information
104 links.clear();
105
106 J = Inertial::MatrixType::Zero();
107 M = T;
108
109 AddLink(link, math::SE3::Identity());
110 }
111
112 int AddLink(const std::shared_ptr<Link>& link, const math::SE3::MatrixType& M_wrt_p) {
113 Inertial::MatrixType J_wrt_p = Inertial::Transform(M_wrt_p, link->I_);
114 int idx = links.size();
115 SubLink_ l;
116 l.link = link;
117 l.J_wrt_p = J_wrt_p;
118 l.M_wrt_p = M_wrt_p;
119 l.M_wrt_base = M * M_wrt_p;
120 links.push_back(l);
121 I += J_wrt_p;
122 J += Inertial::Transform(M, J_wrt_p);
123 return idx;
124 }
125 };
126
127 struct Joint_ {
128 std::shared_ptr<Joint> joint{nullptr};
129 math::se3v::MatrixType S{math::se3v::MatrixType::Zero()}; // zero pose
130
131 int parent_link_idx{};
132 int child_link_idx{};
133 };
134
135 struct LinkIdx_ {
136 int link_idx;
137 int sub_link_idx;
138 };
139
140 explicit Robot(const RobotConfiguration& robot_configuration) { Build(robot_configuration); }
141
142 std::shared_ptr<const Link> GetBase() const { return links_[0].links[0].link; }
143
144 std::shared_ptr<Link> GetBase() { return links_[0].links[0].link; }
145
146 std::vector<std::string> GetLinkNames() const {
147 std::vector<std::string> names;
148 for (const auto& [n, idx] : link_idx_) {
149 names.push_back(n);
150 }
151 return names;
152 }
153
154 std::vector<std::string> GetJointNames() const {
155 std::vector<std::string> names;
156 for (const auto& [n, idx] : joint_idx_) {
157 names.push_back(n);
158 }
159 return names;
160 }
161
162 std::shared_ptr<Link> GetLink(const std::string& name) const {
163 if (link_idx_.find(name) == link_idx_.end()) {
164 return nullptr;
165 }
166 const auto& idx = link_idx_.at(name);
167 return links_[idx.link_idx].links[idx.sub_link_idx].link;
168 }
169
170 std::shared_ptr<Link> GetLink(std::shared_ptr<State<DOF>> state, int index) const {
171 if (index >= (int)state->utr_link_map.size()) {
172 return nullptr;
173 }
174 const auto& idx = state->utr_link_map[index];
175 return links_[idx.link_idx].links[idx.sub_link_idx].link;
176 }
177
178 template <typename LinkContainer = std::vector<std::string>, typename JointContainer = std::vector<std::string>>
179 std::shared_ptr<State<DOF>> MakeState(const LinkContainer& link_names, const JointContainer& joint_names) {
180 static_assert(std::is_same_v<typename LinkContainer::value_type, std::string> ||
181 std::is_same_v<typename LinkContainer::value_type, std::string_view>,
182 "LinkContainer value_type must be std::string");
183 static_assert(std::is_same_v<typename JointContainer::value_type, std::string> ||
184 std::is_same_v<typename JointContainer::value_type, std::string_view>,
185 "JointContainer value_type must be std::string");
186
187 std::vector<bool> flag;
188 flag.resize(n_joints_);
189 std::fill(flag.begin(), flag.end(), false);
190
191 auto state = std::shared_ptr<State<DOF>>(new State<DOF>(n_joints_));
192 for (int i = 0; i < (int)joint_names.size(); i++) {
193 const auto& name = joint_names[i];
194
195 auto it = std::find_if(joints_.begin(), joints_.end(), [name](const auto& j) { return j.joint->name_ == name; });
196 if (it == joints_.end()) {
197 throw std::runtime_error("Cannot find the joint with name");
198 }
199 state->utr_joint_map[i] = it - joints_.begin();
200 state->joint_names[i] = it->joint->name_;
201 flag[state->utr_joint_map[i]] = true;
202 }
203 for (unsigned int i = joint_names.size(); i < n_joints_; i++) {
204 auto it = std::find(flag.begin(), flag.end(), false);
205 state->utr_joint_map[i] = it - flag.begin();
206 state->joint_names[i] = (joints_.begin() + (it - flag.begin()))->joint->name_;
207 *it = true;
208 }
209 for (int i = 0; i < state->utr_joint_map.size(); i++) {
210 state->rtu_joint_map[state->utr_joint_map[i]] = i;
211 }
212
213 std::unordered_map<std::string, bool> name_flag;
214 state->link_names.clear();
215 state->utr_link_map.clear();
216 for (const auto& name : link_names) {
217 auto it = link_idx_.find(name);
218 if (it == link_idx_.end()) {
219 throw std::runtime_error("The link with the given name does not exist");
220 }
221 state->utr_link_map.push_back(it->second);
222 state->link_names.push_back(name);
223 name_flag[name] = true;
224 }
225 for (const auto& [k, v] : link_idx_) {
226 if (name_flag.find(k) == name_flag.end()) {
227 state->utr_link_map.push_back(v);
228 state->link_names.push_back(k);
229 }
230 }
231 for (int i = 0; i < (int)state->link_names.size(); i++) {
232 const auto& n = state->link_names[i];
233 auto it = link_idx_.find(n);
234 if (it == link_idx_.end()) {
235 throw std::runtime_error("Fatal error; link names in state should be found in link idx");
236 }
237 if (it->second.link_idx == 0 && it->second.sub_link_idx == 0) {
238 state->base_link_user_idx = i;
239 }
240 }
241
242 return state;
243 }
244
245 int GetDOF() const { return n_joints_; }
246
247 int GetNumberOfJoints() const { return n_joints_; }
248
249 void ComputeForwardKinematics(std::shared_ptr<State<DOF>> state) {
250 for (int i = 0; i < (int)n_joints_; i++) {
251 state->E[i] = math::SE3::Exp(joints_[i].S, state->q(i));
252
253 int parent_joint_idx = links_[joints_[i].parent_link_idx].parent_joint_idx;
254 state->T[i] = GetJointT(state, parent_joint_idx) * state->E[i];
255
256 state->S.col(i) = math::SE3::Ad(GetJointT(state, parent_joint_idx), joints_[i].S);
257 }
258 }
259
260 void ComputeDiffForwardKinematics(std::shared_ptr<State<DOF>> state) { // In Body Frame
261 for (int i = 0; i < (int)n_joints_; i++) {
262 state->V.col(i) = joints_[i].S * state->qdot(i);
263
264 int parent_joint_idx = links_[joints_[i].parent_link_idx].parent_joint_idx;
265 state->V.col(i) += math::SE3::InvAd(state->E[i], GetJointV(state, parent_joint_idx));
266 }
267 }
268
269 void Compute2ndDiffForwardKinematics(std::shared_ptr<State<DOF>> state) {
270 for (int i = 0; i < (int)n_joints_; i++) {
271 state->Vdot.col(i) = joints_[i].S * state->qddot(i);
272
273 int parent_joint_idx = links_[joints_[i].parent_link_idx].parent_joint_idx;
274 state->Vdot.col(i) += math::SE3::InvAd(state->E[i], GetJointVdot(state, parent_joint_idx)) +
275 math::SE3::ad(state->V.col(i), joints_[i].S * state->qdot(i));
276 }
277 }
278
279 void ComputeInverseDynamics(std::shared_ptr<State<DOF>> state) {
280 for (int i = n_joints_ - 1; i >= 0; i--) {
281 Eigen::Vector<double, 6> F;
282 F.setZero();
283
284 for (const auto& child_joint_idx : links_[joints_[i].child_link_idx].child_joint_idx) {
285 F += math::SE3::InvAd(state->E[child_joint_idx]).transpose() * state->F.col(child_joint_idx);
286 }
287 F += links_[joints_[i].child_link_idx].J * state->Vdot.col(i) -
288 math::SE3::adTranspose(state->V.col(i), links_[joints_[i].child_link_idx].J * state->V.col(i));
289
290 state->F.col(i) = F;
291 state->tau(i) = joints_[i].S.dot(F);
292 }
293 }
294
295 Eigen::Vector<double, DOF> ComputeGravityTerm(std::shared_ptr<State<DOF>> state) {
296 Eigen::Vector<double, DOF> gravity_term;
297 gravity_term.resize(n_joints_);
298 std::vector<Eigen::Vector<double, 6>> Vdot{}, Fs{};
299 Vdot.resize(n_joints_);
300 Fs.resize(n_joints_);
301
302 // Calculate Vdot
303 for (int i = 0; i < (int)n_joints_; i++) {
304 int parent_joint_idx = links_[joints_[i].parent_link_idx].parent_joint_idx;
305 if (parent_joint_idx < 0) {
306 Vdot[i] = math::SE3::InvAd(state->E[i], state->Vdot0);
307 } else {
308 Vdot[i] = math::SE3::InvAd(state->E[i], Vdot[parent_joint_idx]);
309 }
310 }
311
312 //
313 for (int i = n_joints_ - 1; i >= 0; i--) {
314 Eigen::Vector<double, 6> F;
315 F.setZero();
316
317 for (const auto& child_joint_idx : links_[joints_[i].child_link_idx].child_joint_idx) {
318 F += math::SE3::InvAd(state->E[child_joint_idx]).transpose() * Fs[child_joint_idx];
319 }
320 F += links_[joints_[i].child_link_idx].J * Vdot[i];
321
322 Fs[i] = F;
323 gravity_term(state->rtu_joint_map[i]) = joints_[i].S.dot(F);
324 }
325
326 return gravity_term;
327 }
328
329 Eigen::Matrix<double, DOF, DOF> ComputeMassMatrix(std::shared_ptr<State<DOF>> state) {
330 Eigen::Matrix<double, DOF, DOF> M(n_joints_, n_joints_);
331 M.setZero();
332
333 std::vector<Eigen::Matrix<double, 6, DOF>> J;
334 J.resize(n_joints_);
335
336 for (int i = 0; i < (int)n_joints_; i++) {
337 int parent_joint_idx = links_[joints_[i].parent_link_idx].parent_joint_idx;
338 if (parent_joint_idx < 0) {
339 J[i].setZero();
340 J[i].col(state->rtu_joint_map[i]) = joints_[i].S;
341 } else {
342 J[i] = math::SE3::Ad(state->E[i].inverse()) * J[parent_joint_idx];
343 J[i].col(state->rtu_joint_map[i]) = joints_[i].S;
344 }
345 }
346
347 for (int i = 0; i < (int)n_joints_; i++) {
348 M += J[i].transpose() * links_[joints_[i].child_link_idx].J * J[i];
349 }
350
351 return M;
352 }
353
354 Eigen::Matrix<double, 6, 6> ComputeReflectiveInertia(std::shared_ptr<State<DOF>> state, unsigned int from,
355 unsigned int to) {
356 if (from >= state->utr_link_map.size() || to >= state->utr_link_map.size()) {
357 throw std::runtime_error("Out of range state link");
358 }
359 const auto& J = ComputeBodyJacobian(state, from, to);
360 Eigen::Matrix<double, DOF, DOF> m_inv = ComputeMassMatrix(state).completeOrthogonalDecomposition().pseudoInverse();
361 return (J * m_inv * J.transpose()).completeOrthogonalDecomposition().pseudoInverse();
362 }
363
364 math::SE3::MatrixType ComputeTransformation(std::shared_ptr<State<DOF>> state, unsigned int from, unsigned int to) {
365 if (from >= state->utr_link_map.size() || to >= state->utr_link_map.size()) {
366 throw std::runtime_error("Out of range state link");
367 }
368 return GetTransformation(state, state->utr_link_map[from], state->utr_link_map[to]);
369 }
370
371 math::se3v::MatrixType ComputeBodyVelocity(std::shared_ptr<State<DOF>> state, unsigned int from, unsigned int to) {
372 if (from >= state->utr_link_map.size() || to >= state->utr_link_map.size()) {
373 throw std::runtime_error("Out of range state link");
374 }
375 math::SE3::MatrixType T_from_to = ComputeTransformation(state, from, to);
376 math::se3v::MatrixType V_from = GetLinkV(state, state->utr_link_map[from]);
377 math::se3v::MatrixType V_to = GetLinkV(state, state->utr_link_map[to]);
378 return V_to - math::SE3::InvAd(T_from_to, V_from);
379 }
380
381 Eigen::Matrix<double, 6, DOF> ComputeSpaceJacobian(std::shared_ptr<State<DOF>> state, unsigned int from,
382 unsigned int to) {
383 return GetSpaceJacobian(state, state->utr_link_map[from], state->utr_link_map[to]);
384 }
385
386 Eigen::Matrix<double, 6, DOF> ComputeBodyJacobian(std::shared_ptr<State<DOF>> state, unsigned int from,
387 unsigned int to) {
388 math::SE3::MatrixType T = ComputeTransformation(state, from, to);
389 return math::SE3::InvAd(T) * ComputeSpaceJacobian(state, from, to);
390 }
391
392 double ComputeMass(std::shared_ptr<State<DOF>> state, unsigned int target_link) {
393 const auto& idx = state->utr_link_map[target_link];
394 return Inertial::GetMass(links_[idx.link_idx].links[idx.sub_link_idx].link->I_);
395 }
396
397 Eigen::Vector3d ComputeCenterOfMass(std::shared_ptr<State<DOF>> state, unsigned int ref_link,
398 unsigned int target_link) {
399 const auto& idx = state->utr_link_map[target_link];
400 return math::SE3::Multiply(ComputeTransformation(state, ref_link, target_link),
401 Inertial::GetCOM(links_[idx.link_idx].links[idx.sub_link_idx].link->I_));
402 }
403
404 Eigen::Vector3d ComputeCenterOfMass(std::shared_ptr<State<DOF>> state, unsigned int ref_link,
405 const std::vector<unsigned int>& target_links) {
406 Eigen::Vector3d com{Eigen::Vector3d ::Zero()};
407 double mass = 0;
408 for (auto target_link : target_links) {
409 const auto& idx = state->utr_link_map[target_link];
410 const auto& p = Inertial::GetCOM(links_[idx.link_idx].links[idx.sub_link_idx].link->I_);
411 double m = Inertial::GetMass(links_[idx.link_idx].links[idx.sub_link_idx].link->I_);
412 com += math::SE3::Multiply(ComputeTransformation(state, ref_link, target_link), p) * m;
413 mass += m;
414 }
415 return com / mass;
416 }
417
418 Eigen::Matrix<double, 3, DOF> ComputeCenterOfMassJacobian(std::shared_ptr<State<DOF>> state, unsigned int ref_link,
419 unsigned int target_link) {
420 using namespace math;
421
422 const auto& idx = state->utr_link_map[target_link];
423 return SE3::GetRotation(ComputeTransformation(state, ref_link, target_link)) *
424 (SE3::InvAd(SE3::T(Inertial::GetCOM(links_[idx.link_idx].links[idx.sub_link_idx].link->I_))) *
425 ComputeBodyJacobian(state, ref_link, target_link))
426 .block(3, 0, 3, n_joints_);
427 }
428
435 Inertial::MatrixType ComputeTotalInertial(std::shared_ptr<State<DOF>> state, unsigned int ref_link) {
436 Inertial::MatrixType I{Inertial::MatrixType::Zero()};
437 for (const auto& link : links_) {
438 I += Inertial::Transform(GetJointT(state, link.parent_joint_idx), link.J);
439 }
440
441 return Inertial::Transform(math::SE3::Inverse(GetLinkT(state, state->utr_link_map[ref_link])), I);
442 }
443
444 Eigen::Vector3d ComputeCenterOfMass(std::shared_ptr<State<DOF>> state, unsigned int ref_link) {
445 Eigen::Vector3d sum{Eigen::Vector3d::Zero()};
446 double mass = 0;
447
448 for (const auto& link : links_) {
449 sum += Inertial::GetMass(link.J) *
450 math::SE3::Multiply(GetJointT(state, link.parent_joint_idx), Inertial::GetCOM(link.J));
451 mass += Inertial::GetMass(link.J);
452 }
453
454 return math::SE3::Multiply(math::SE3::Inverse(GetLinkT(state, state->utr_link_map[ref_link])), sum / mass);
455 }
456
457 Eigen::Matrix<double, 3, DOF> ComputeCenterOfMassJacobian(std::shared_ptr<State<DOF>> state, unsigned int ref_link) {
458 using namespace math;
459 Eigen::Matrix<double, 3, DOF> J;
460 J.resize(3, n_joints_);
461 J.setZero();
462 double mass = 0;
463 for (int i = 0; i < (int)links_.size(); i++) {
464 // const auto& link = links_[i];
465 double m = Inertial::GetMass(links_[i].I);
466 J += m * SE3::GetRotation(GetTransformation(state, state->utr_link_map[ref_link], {i, 0})) *
467 (SE3::InvAd(SE3::T(Inertial::GetCOM(links_[i].I))) *
468 GetBodyJacobian(state, state->utr_link_map[ref_link], {i, 0}))
469 .block(3, 0, 3, n_joints_);
470 mass += m;
471 }
472 return J / mass;
473 }
474
475 std::vector<CollisionResult> DetectCollisionsOrNearestLinks(std::shared_ptr<State<DOF>> state,
476 int collision_threshold = 0) {
477 std::vector<CollisionResult> dis;
478
479 int n = state->GetLinkNames().size();
480 for (int i = 0; i < n; i++) {
481 auto link1 = GetLink(state, i);
482 if (!link1) {
483 throw std::runtime_error("Index error");
484 }
485 auto link1_T = ComputeTransformation(state, 0, i);
486 for (int j = i + 1; j < n; j++) {
487 auto link2 = GetLink(state, j);
488 if (!link2) {
489 throw std::runtime_error("Index error");
490 }
491 auto link2_T = ComputeTransformation(state, 0, j);
492
493 for (auto link1_col : link1->GetCollisions()) {
494 for (auto link2_col : link2->GetCollisions()) {
495
496 for (auto link1_geom : link1_col->GetGeoms()) {
497 for (auto link2_geom : link2_col->GetGeoms()) {
498
499 if (link1_geom->GetType() == GeomType::kCapsule && //
500 link2_geom->GetType() == GeomType::kCapsule && //
501 link1_geom->Filter(*link2_geom)) {
502 auto collision_result =
503 link1_geom->ComputeMinimumDistance(link1_T * link1_col->GetOrigin(), *link2_geom, //
504 link2_T * link2_col->GetOrigin());
505 if (collision_result.has_value()) {
506 auto v = collision_result.value();
507 v.link1 = link1->GetName();
508 v.link2 = link2->GetName();
509 dis.push_back(v);
510 }
511 }
512 }
513 }
514 }
515 }
516 }
517 }
518
519 std::sort(dis.begin(), dis.end(),
520 [](const CollisionResult& r1, const CollisionResult& r2) { return r1.distance < r2.distance; });
521
522 std::vector<CollisionResult> rv;
523 for (const auto& d : dis) {
524 if (d.distance > 0 && (int)rv.size() >= collision_threshold) {
525 break;
526 }
527 rv.push_back(d);
528 }
529 return rv;
530 }
531
532 // TODO: ComputeBodyJacobianDot
533 // TODO: ComputeBodyAcceleration
534 // TODO: InverseDiffDynamics
535
536 Eigen::Vector<double, DOF> GetLimitQLower(const std::shared_ptr<State<DOF>>& state) {
537 return GetJointProperty(state, [](auto j) { return j->GetLimitQLower(); });
538 }
539
540 Eigen::Vector<double, DOF> GetLimitQUpper(const std::shared_ptr<State<DOF>>& state) {
541 return GetJointProperty(state, [](auto j) { return j->GetLimitQUpper(); });
542 }
543
544 Eigen::Vector<double, DOF> GetLimitQdotLower(const std::shared_ptr<State<DOF>>& state) {
545 return GetJointProperty(state, [](auto j) { return j->GetLimitQdotLower(); });
546 }
547
548 Eigen::Vector<double, DOF> GetLimitQdotUpper(const std::shared_ptr<State<DOF>>& state) {
549 return GetJointProperty(state, [](auto j) { return j->GetLimitQdotUpper(); });
550 }
551
552 Eigen::Vector<double, DOF> GetLimitQddotLower(const std::shared_ptr<State<DOF>>& state) {
553 return GetJointProperty(state, [](auto j) { return j->GetLimitQddotLower(); });
554 }
555
556 Eigen::Vector<double, DOF> GetLimitQddotUpper(const std::shared_ptr<State<DOF>>& state) {
557 return GetJointProperty(state, [](auto j) { return j->GetLimitQddotUpper(); });
558 }
559
560 Eigen::Vector<double, DOF> GetLimitTorque(const std::shared_ptr<State<DOF>>& state) {
561 return GetJointProperty(state, [](auto j) { return j->GetLimitTorque(); });
562 }
563
564 Eigen::Vector<double, DOF> GetJointProperty(const std::shared_ptr<State<DOF>>& state,
565 const std::function<double(std::shared_ptr<Joint>)>& getter) {
566 Eigen::Vector<double, DOF> prop;
567 prop.resize(n_joints_);
568 for (int i = 0; i < (int)n_joints_; i++) {
569 prop(state->rtu_joint_map[i]) = getter(joints_[i].joint);
570 }
571 return prop;
572 }
573
574 void ComputeMobilityInverseDiffKinematics(std::shared_ptr<State<DOF>> state, //
575 const Eigen::Vector2d& linear_velocity, // (m/s)
576 double angular_velocity // (rad/s)
577 ) {
578 math::se2v::MatrixType S;
579 S(0) = angular_velocity;
580 S.tail<2>() = linear_velocity;
581
582 ComputeMobilityInverseDiffKinematics(state, S);
583 }
584
585 void ComputeMobilityInverseDiffKinematics(std::shared_ptr<State<DOF>> state, //
586 const math::se2v::MatrixType& body_velocity // w, x, y
587 ) {
588 math::se3v::MatrixType S{math::se3v::MatrixType::Zero()};
589 S.block<3, 1>(2, 0) = body_velocity;
590
591 S = math::SE3::InvAd(mobile_base_->T, S);
592
593 switch (mobile_base_->type) {
594 case MobileBaseType::kNone: {
595 break;
596 }
597 case MobileBaseType::kDifferential: {
598 auto mb = std::static_pointer_cast<MobileBaseDifferential>(mobile_base_);
599 double v_right = S(3) + mb->wheel_base / 2 * S(2);
600 double v_left = S(3) - mb->wheel_base / 2 * S(2);
601 state->qdot(mb->right_wheel_idx) = -v_right / mb->wheel_radius;
602 state->qdot(mb->left_wheel_idx) = -v_left / mb->wheel_radius;
603 break;
604 }
605 case MobileBaseType::kMecanum: {
606 double v_x = S(3);
607 double v_y = S(4);
608 double w = S(2);
609 // std::cout<<"V_x: "<< v_x<<std::endl;
610 // std::cout<<"V_y: "<< v_y<<std::endl;
611 // std::cout<<"w: "<< w<<std::endl;
612
613 auto mb = std::static_pointer_cast<MobileBaseMecanum>(mobile_base_);
614 double w_1 = 1 / mb->wheel_radius * (v_x - v_y - (mb->L_x + mb->L_y) * w);
615 double w_2 = 1 / mb->wheel_radius * (v_x + v_y + (mb->L_x + mb->L_y) * w);
616 double w_3 = 1 / mb->wheel_radius * (v_x + v_y - (mb->L_x + mb->L_y) * w);
617 double w_4 = 1 / mb->wheel_radius * (v_x - v_y + (mb->L_x + mb->L_y) * w);
618 state->qdot(mb->fl_wheel_idx) = w_1;
619 state->qdot(mb->fr_wheel_idx) = w_2;
620 state->qdot(mb->rl_wheel_idx) = w_3;
621 state->qdot(mb->rr_wheel_idx) = w_4;
622
623 // std::cout<<"mb->fl_wheel_idx: "<<mb->fl_wheel_idx<<std::endl;
624 // std::cout<<"mb->fr_wheel_idx: "<<mb->fr_wheel_idx<<std::endl;
625 // std::cout<<"mb->rl_wheel_idx: "<<mb->rl_wheel_idx<<std::endl;
626 // std::cout<<"mb->rr_wheel_idx: "<<mb->rr_wheel_idx<<std::endl;
627
628 break;
629 }
630 }
631 }
632
633 math::se2v::MatrixType ComputeMobilityDiffKinematics( //
634 std::shared_ptr<State<DOF>> state //
635 ) {
636 math::se3v::MatrixType S{math::se3v::MatrixType::Zero()};
637
638 switch (mobile_base_->type) {
639 case MobileBaseType::kNone: {
640 break;
641 }
642 case MobileBaseType::kDifferential: {
643 auto mb = std::static_pointer_cast<MobileBaseDifferential>(mobile_base_);
644 double w_r = -state->qdot(mb->right_wheel_idx);
645 double w_l = -state->qdot(mb->left_wheel_idx);
646 S.block<3, 1>(2, 0) = math::se2v::MatrixType{(w_r - w_l) * mb->wheel_radius / mb->wheel_base,
647 (w_r + w_l) * mb->wheel_radius / 2, 0};
648 break;
649 }
650 case MobileBaseType::kMecanum: {
651 auto mb = std::static_pointer_cast<MobileBaseMecanum>(mobile_base_);
652 double w_1 = state->qdot(mb->fl_wheel_idx);
653 double w_2 = state->qdot(mb->fr_wheel_idx);
654 double w_3 = state->qdot(mb->rl_wheel_idx);
655 double w_4 = state->qdot(mb->rr_wheel_idx);
656 double v_x = mb->wheel_radius / 4 * (w_1 + w_2 + w_3 + w_4);
657 double v_y = mb->wheel_radius / 4 * (-w_1 + w_2 + w_3 - w_4);
658 double w = mb->wheel_radius / (4 * (mb->L_x + mb->L_y)) * (-w_1 + w_2 - w_3 + w_4);
659 S.block<3, 1>(2, 0) = math::se2v::MatrixType{w, v_x, v_y};
660 break;
661 }
662 }
663
664 S = math::SE3::Ad(mobile_base_->T, S);
665
666 return S.block<3, 1>(2, 0);
667 }
668
669 static int CountJoints(const std::shared_ptr<Link>& base_link, bool include_fixed = false) {
670 int n_joints = 0;
671
672 std::queue<std::shared_ptr<Link>> que;
673 que.push(base_link);
674 while (!que.empty()) {
675 std::shared_ptr<Link> link = que.front();
676 que.pop();
677
678 for (const auto& joint : link->GetChildJointList()) {
679 que.push(joint->GetChildLink());
680
681 if (joint->IsFixed() && !include_fixed)
682 ;
683 else {
684 n_joints++;
685 }
686 }
687 }
688 return n_joints;
689 }
690
691 protected:
692 Robot() = default;
693
694 void Build(const RobotConfiguration& rc) {
695 const auto& base = rc.base_link;
696
697 int n_joints = CountJoints(base);
698
699 if constexpr (DOF < 0) {
700 n_links_ = n_joints + 1;
701 n_joints_ = n_joints;
702
703 links_.resize(n_links_);
704 joints_.resize(n_joints_);
705 } else {
706 if (n_joints != DOF) {
707 throw std::runtime_error("DOF does not match the number of joints");
708 }
709 n_links_ = DOF + 1;
710 n_joints_ = DOF;
711 }
712
713 struct QueueItem {
714 int parent_joint_idx{};
715 int parent_link_idx{};
716 std::shared_ptr<Link> link;
717 int depth{};
718 bool merge_parent_link{};
719 math::SE3::MatrixType M_wrt_base;
720 math::SE3::MatrixType M_wrt_p;
721 };
722
723 std::queue<QueueItem> que;
724 int link_idx = 0;
725 int joint_idx = 0;
726 {
727 {
728 QueueItem item;
729 item.parent_joint_idx = -1;
730 item.parent_link_idx = link_idx++;
731 item.link = base;
732 item.depth = 0;
733 item.merge_parent_link = false;
734 item.M_wrt_base = math::SE3::Identity();
735 item.M_wrt_p = math::SE3::Identity();
736 que.push(item);
737 }
738
739 while (!que.empty()) {
740 auto e = que.front();
741 que.pop();
742
743 if (!e.merge_parent_link) {
744 links_[e.parent_link_idx].SetBaseLink(e.link, e.M_wrt_base);
745 links_[e.parent_link_idx].depth = e.depth;
746 links_[e.parent_link_idx].parent_joint_idx = e.parent_joint_idx;
747 link_idx_[e.link->name_] = {e.parent_link_idx, 0};
748 } else {
749 int sub_link_idx = links_[e.parent_link_idx].AddLink(e.link, e.M_wrt_p);
750 link_idx_[e.link->name_] = {e.parent_link_idx, sub_link_idx};
751 }
752
753 for (const auto& joint : e.link->child_joints_) {
754 if (!joint->fixed_) {
755 math::SE3::MatrixType M_wrt_base = e.M_wrt_base * joint->T_pj_;
756 {
757 Joint_ j;
758 j.joint = joint;
759 j.S = math::SE3::Ad(M_wrt_base, joint->S_);
760 j.parent_link_idx = e.parent_link_idx;
761 j.child_link_idx = link_idx;
762 joints_[joint_idx] = j;
763 }
764 joint_idx_[joint->name_] = joint_idx;
765 links_[e.parent_link_idx].child_joint_idx.push_back(joint_idx);
766 {
767 QueueItem item;
768 item.parent_joint_idx = joint_idx++;
769 item.parent_link_idx = link_idx++;
770 item.link = joint->child_link_;
771 item.depth = e.depth + 1;
772 item.merge_parent_link = false;
773 item.M_wrt_base = M_wrt_base;
774 item.M_wrt_p = math::SE3::Identity();
775 que.push(item);
776 }
777 } else {
778 QueueItem item;
779 item.parent_joint_idx = e.parent_joint_idx;
780 item.parent_link_idx = e.parent_link_idx;
781 item.link = joint->child_link_;
782 item.depth = e.depth;
783 item.merge_parent_link = true;
784 item.M_wrt_base = e.M_wrt_base * joint->T_pj_ * joint->T_jc_;
785 item.M_wrt_p = e.M_wrt_p * joint->T_pj_ * joint->T_jc_;
786 que.push(item);
787 }
788 }
789 }
790 }
791
792 if (rc.mobile_base) {
793 switch (rc.mobile_base->type) {
794 case MobileBaseType::kNone: {
795 auto mb = std::make_shared<MobileBase>();
796 *std::static_pointer_cast<MobileBase>(mb) = *rc.mobile_base;
797 mobile_base_ = mb;
798 break;
799 }
800 case MobileBaseType::kDifferential: {
801 auto mb = std::make_shared<MobileBaseDifferential>();
802 *std::static_pointer_cast<MobileBase>(mb) = *rc.mobile_base;
803 if (rc.mobile_base->joints.size() != 2) {
804 throw std::runtime_error("Differential type mobile should have two joints.");
805 }
806 if (joint_idx_.find(rc.mobile_base->joints[0]) == joint_idx_.end()) {
807 throw std::runtime_error("Right wheel has invalid parameter.");
808 }
809 if (joint_idx_.find(rc.mobile_base->joints[1]) == joint_idx_.end()) {
810 throw std::runtime_error("Left wheel has invalid parameter.");
811 }
812 if (rc.mobile_base->params.size() != 2) {
813 throw std::runtime_error("Differential type mobile should have two parameters.");
814 }
815 mb->right_wheel_idx = joint_idx_[rc.mobile_base->joints[0]];
816 mb->left_wheel_idx = joint_idx_[rc.mobile_base->joints[1]];
817 mb->wheel_base = rc.mobile_base->params[0];
818 mb->wheel_radius = rc.mobile_base->params[1];
819 mobile_base_ = mb;
820 break;
821 }
822 case MobileBaseType::kMecanum:
823 auto mb = std::make_shared<MobileBaseMecanum>();
824 *std::static_pointer_cast<MobileBase>(mb) = *rc.mobile_base;
825 if (rc.mobile_base->joints.size() != 4) {
826 throw std::runtime_error(
827 "Mecanum type mobile should have four joints. (front-right, front-left, rear-right, rear-left)");
828 }
829 if (joint_idx_.find(rc.mobile_base->joints[0]) == joint_idx_.end()) {
830 throw std::runtime_error("Front-right wheel has invalid parameter.");
831 }
832 if (joint_idx_.find(rc.mobile_base->joints[1]) == joint_idx_.end()) {
833 throw std::runtime_error("Front-left wheel has invalid parameter.");
834 }
835 if (joint_idx_.find(rc.mobile_base->joints[2]) == joint_idx_.end()) {
836 throw std::runtime_error("Rear-right wheel has invalid parameter.");
837 }
838 if (joint_idx_.find(rc.mobile_base->joints[3]) == joint_idx_.end()) {
839 throw std::runtime_error("Rear-left wheel has invalid parameter.");
840 }
841 if (rc.mobile_base->params.size() != 3) {
842 throw std::runtime_error("Mecanum type mobile should have three parameters. (Lx, Ly, wheel radius)");
843 }
844 mb->fr_wheel_idx = joint_idx_[rc.mobile_base->joints[0]];
845 mb->fl_wheel_idx = joint_idx_[rc.mobile_base->joints[1]];
846 mb->rr_wheel_idx = joint_idx_[rc.mobile_base->joints[2]];
847 mb->rl_wheel_idx = joint_idx_[rc.mobile_base->joints[3]];
848 mb->L_x = rc.mobile_base->params[0];
849 mb->L_y = rc.mobile_base->params[1];
850 mb->wheel_radius = rc.mobile_base->params[2];
851 mobile_base_ = mb;
852 break;
853 }
854 }
855 }
856
857 math::SE3::MatrixType GetLinkT(std::shared_ptr<State<DOF>> state, const LinkIdx_& idx) {
858 return GetJointT(state, links_[idx.link_idx].parent_joint_idx) *
859 links_[idx.link_idx].links[idx.sub_link_idx].M_wrt_base;
860 }
861
862 math::se3v::MatrixType GetLinkV(std::shared_ptr<State<DOF>> state, const LinkIdx_& idx) {
863 return math::SE3::InvAd(links_[idx.link_idx].links[idx.sub_link_idx].M_wrt_base,
864 GetJointV(state, links_[idx.link_idx].parent_joint_idx));
865 }
866
867 math::SE3::MatrixType GetJointT(std::shared_ptr<State<DOF>> state, int joint_idx) {
868 if (joint_idx < 0)
869 return math::SE3::Identity();
870 return state->T[joint_idx];
871 }
872
873 math::se3v::MatrixType GetJointV(std::shared_ptr<State<DOF>> state, int joint_idx) {
874 if (joint_idx < 0)
875 return state->V0;
876 return state->V.col(joint_idx);
877 }
878
879 math::se3v::MatrixType GetJointVdot(std::shared_ptr<State<DOF>> state, int joint_idx) {
880 if (joint_idx < 0)
881 return state->Vdot0;
882 return state->Vdot.col(joint_idx);
883 }
884
885 math::SE3::MatrixType GetTransformation(std::shared_ptr<State<DOF>> state, const LinkIdx_& from, const LinkIdx_& to) {
886 return math::SE3::Inverse(GetLinkT(state, from)) * GetLinkT(state, to);
887 }
888
889 Eigen::Matrix<double, 6, DOF> GetSpaceJacobian(std::shared_ptr<State<DOF>> state, const LinkIdx_& from,
890 const LinkIdx_& to) {
891 Eigen::Matrix<double, 6, DOF> J;
892 J.resize(6, n_joints_);
893 J.setZero();
894
895 // TODO: Need to optimize
896
897 int from_link_idx = from.link_idx;
898 int to_link_idx = to.link_idx;
899 while (from_link_idx != to_link_idx) {
900 if (links_[from_link_idx].depth > links_[to_link_idx].depth) {
901 int pj = links_[from_link_idx].parent_joint_idx;
902 J.col(state->rtu_joint_map[pj]) = -state->S.col(pj);
903 from_link_idx = joints_[pj].parent_link_idx;
904 } else {
905 int pj = links_[to_link_idx].parent_joint_idx;
906 J.col(state->rtu_joint_map[pj]) = state->S.col(pj);
907 to_link_idx = joints_[pj].parent_link_idx;
908 }
909 }
910
911 return math::SE3::InvAd(GetLinkT(state, from)) * J;
912 }
913
914 Eigen::Matrix<double, 6, DOF> GetBodyJacobian(std::shared_ptr<State<DOF>> state, const LinkIdx_& from,
915 const LinkIdx_& to) {
916 math::SE3::MatrixType T = GetTransformation(state, from, to);
917 return math::SE3::InvAd(T) * GetSpaceJacobian(state, from, to);
918 }
919
920 private:
921 ContainerType<Link_, DOF + 1> links_;
922 ContainerType<Joint_, DOF> joints_;
923
924 std::unordered_map<std::string, LinkIdx_> link_idx_; // link name to (parent idx, sub link idx)
925 std::unordered_map<std::string, unsigned int> joint_idx_; // joint name to joint idx
926
927 size_t n_links_{};
928 size_t n_joints_{};
929
933 std::shared_ptr<MobileBase> mobile_base_;
934};
935
936RBY1_SDK_API RobotConfiguration LoadRobotFromURDFData(const std::string& model, const std::string& base_link_name);
937
938RBY1_SDK_API RobotConfiguration LoadRobotFromURDF(const std::string& path, const std::string& base_link_name);
939
940RBY1_SDK_API inline std::string to_string(GeomType type) {
941 switch (type) {
942 case GeomType::kCapsule:
943 return "capsule";
944 }
945 return "unknown";
946}
947
948} // namespace rb::dyn
Definition state.h:10
Inertial::MatrixType ComputeTotalInertial(std::shared_ptr< State< DOF > > state, unsigned int ref_link)
Definition robot.h:435
Definition state.h:13
Definition robot.h:49
Definition robot.h:41
Definition robot.h:57
Definition robot.h:127
Definition robot.h:72