1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2018 projectchrono.org
5 // All right reserved.
6 //
7 // Use of this source code is governed by a BSD-style license that can be found
8 // in the LICENSE file at the top level of the distribution and at
9 // http://projectchrono.org/license-chrono.txt.
10 //
11 // =============================================================================
12 // Authors: Radu Serban
13 // =============================================================================
14 //
15 // =============================================================================
16
17 #include <cmath>
18
19 #include "chrono/assets/ChBoxShape.h"
20 #include "chrono/assets/ChColorAsset.h"
21 #include "chrono/assets/ChCylinderShape.h"
22 #include "chrono/assets/ChSphereShape.h"
23 #include "chrono/assets/ChTexture.h"
24 #include "chrono/assets/ChTriangleMeshShape.h"
25
26 #include "chrono/motion_functions/ChFunction_Setpoint.h"
27
28 #include "chrono/physics/ChLinkMotorRotationAngle.h"
29 #include "chrono/physics/ChLinkMotorRotationSpeed.h"
30 #include "chrono/physics/ChSystemNSC.h"
31 #include "chrono/physics/ChSystemSMC.h"
32
33 #include "chrono_models/robot/robosimian/RoboSimian.h"
34
35 namespace chrono {
36 namespace robosimian {
37
38 // =============================================================================
39
40 // Concrete Link types
41 // mesh_name, offset, color, mass, com, inertia_xx, inertia_xy, shapes
42
43 const Link FtsLink("robosim_fts",
44 ChVector<>(0, 0, 0),
45 ChColor(1.0f, 0.0f, 0.0f),
46 0.0,
47 ChVector<>(0, 0, 0),
48 ChVector<>(0, 0, 0),
49 ChVector<>(0, 0, 0),
50 {});
51
52 const Link PitchLink("robosim_pitch_link",
53 ChVector<>(0, 0, 0),
54 ChColor(0.4f, 0.7f, 0.4f),
55 0.428770,
56 ChVector<>(-0.050402, 0.012816, 0.000000),
57 ChVector<>(0.000670, 0.000955, 0.000726),
58 ChVector<>(0.000062, 0.000000, 0.000000),
59 {CylinderShape(ChVector<>(-0.07, 0, 0), Q_from_AngZ(CH_C_PI_2), 0.055, 0.07)});
60
61 const Link RollLink("robosim_roll_link",
62 ChVector<>(0, 0, 0),
63 ChColor(0.7f, 0.4f, 0.4f),
64 4.078540,
65 ChVector<>(0.066970, -0.090099, -0.000084),
66 ChVector<>(0.010580, 0.025014, 0.031182),
67 ChVector<>(-0.008765, -0.000002, 0.000007),
68 {CylinderShape(ChVector<>(0.065, -0.12, 0), Q_from_AngZ(CH_C_PI_2), 0.055, 0.24),
69 CylinderShape(ChVector<>(0.0, -0.035, 0), QUNIT, 0.055, 0.075)});
70
71 const Link RollLinkLast("robosim_roll_link",
72 ChVector<>(0, 0, 0),
73 ChColor(0.7f, 0.4f, 0.4f),
74 4.078540,
75 ChVector<>(0.066970, -0.090099, -0.000084),
76 ChVector<>(0.010580, 0.025014, 0.031182),
77 ChVector<>(-0.008765, -0.000002, 0.000007),
78 {CylinderShape(ChVector<>(0.105, -0.12, 0), Q_from_AngZ(CH_C_PI_2), 0.055, 0.32),
79 CylinderShape(ChVector<>(0.0, -0.035, 0), QUNIT, 0.055, 0.075)});
80
81 const Link RollLinkLastWheel("robosim_roll_link_w_wheel",
82 ChVector<>(0, 0, 0),
83 ChColor(0.7f, 0.4f, 0.4f),
84 4.078540,
85 ChVector<>(0.066970, -0.090099, -0.000084),
86 ChVector<>(0.010580, 0.025014, 0.031182),
87 ChVector<>(-0.008765, -0.000002, 0.000007),
88 {CylinderShape(ChVector<>(0.105, -0.12, 0), Q_from_AngZ(CH_C_PI_2), 0.055, 0.32),
89 CylinderShape(ChVector<>(0.0, -0.035, 0), QUNIT, 0.055, 0.075),
90 CylinderShape(ChVector<>(0.0, -0.19, 0), QUNIT, 0.080, 0.0375)});
91
92 const Link FtAdapterLink("robosim_ft_adapter",
93 ChVector<>(0, 0, 0),
94 ChColor(0.4f, 0.4f, 0.4f),
95 0.253735,
96 ChVector<>(-0.00531, -0.00060, -0.001873),
97 ChVector<>(0.00042, 0.00024, 0.00023),
98 ChVector<>(0, 0, 0),
99 {});
100
101 const Link FtLink("robosim_force_torque_sensor",
102 ChVector<>(0, 0, 0),
103 ChColor(0.4f, 0.4f, 0.4f),
104 0.195418,
105 ChVector<>(-0.000135, 0.000118, 0.000084),
106 ChVector<>(0.000086, 0.000056, 0.000057),
107 ChVector<>(0, 0, 0),
108 {});
109
110 const Link WheelMountLink("robosim_wheel_mount",
111 ChVector<>(0.12024, 0, 0),
112 ChColor(0.4f, 0.7f, 0.4f),
113 3.1775,
114 ChVector<>(-0.005260, 0.042308, 0.000088),
115 ChVector<>(0.010977, 0.005330, 0.011405),
116 ChVector<>(0.000702, 0.000026, -0.000028),
117 {CylinderShape(ChVector<>(0.12024, 0.02, 0), QUNIT, 0.0545, 0.175)});
118
119 const Link WheelLink("robosim_wheel",
120 ChVector<>(0, 0, 0),
121 ChColor(0.6f, 0.6f, 0.6f),
122 1.499326,
123 ChVector<>(0.0, 0.0, -0.000229),
124 ChVector<>(0.006378, 0.006377, 0.009155),
125 ChVector<>(0, 0, 0),
126 {CylinderShape(ChVector<>(0, 0, 0), Q_from_AngX(CH_C_PI_2), 0.12, 0.123)});
127
128 // List of links for front and rear legs
129 // name, link, body included?
130
131 const int num_links = 11;
132
133 const LinkData front_links[] = {
134 {"link0", FtsLink, false}, //
135 {"link1", PitchLink, true}, //
136 {"link2", RollLink, true}, //
137 {"link3", PitchLink, true}, //
138 {"link4", RollLink, true}, //
139 {"link5", PitchLink, true}, //
140 {"link6", RollLinkLast, true}, //
141 {"ftadapter_link", FtAdapterLink, true}, //
142 {"ft_link", FtLink, true}, //
143 {"link7", WheelMountLink, true}, //
144 {"link8", WheelLink, true} //
145 };
146
147 const LinkData rear_links[] = {
148 {"link0", FtsLink, false}, //
149 {"link1", PitchLink, true}, //
150 {"link2", RollLink, true}, //
151 {"link3", PitchLink, true}, //
152 {"link4", RollLink, true}, //
153 {"link5", PitchLink, true}, //
154 {"link6", RollLinkLastWheel, true}, //
155 {"ftadapter_link", FtAdapterLink, true}, //
156 {"ft_link", FtLink, true}, //
157 {"link7", WheelMountLink, true}, //
158 {"link8", WheelLink, true} //
159 };
160
161 // List of joints in a limb chain
162 // name, parent_link, child_link, fixed?, xyz, rpy, axis
163
164 const int num_joints = 10;
165
166 const JointData joints[] = {
167
168 {"joint1", "link0", "link1", false, ChVector<>(0.17203, 0.00000, 0.00000), ChVector<>(3.14159, 0.00000, 0.00000),
169 ChVector<>(1, 0, 0)},
170
171 {"joint2", "link1", "link2", false, ChVector<>(0.00000, 0.00000, 0.00000), ChVector<>(0.00000, 0.00000, 0.00000),
172 ChVector<>(0, -1, 0)},
173
174 {"joint3", "link2", "link3", false, ChVector<>(0.28650, -0.11700, 0.00000), ChVector<>(0.00000, 0.00000, 0.00000),
175 ChVector<>(1, 0, 0)},
176
177 {"joint4", "link3", "link4", false, ChVector<>(0.00000, 0.00000, 0.00000), ChVector<>(0.00000, 0.00000, 0.00000),
178 ChVector<>(0, -1, 0)},
179
180 {"joint5", "link4", "link5", false, ChVector<>(0.28650, -0.11700, 0.00000), ChVector<>(0.00000, 0.00000, 0.00000),
181 ChVector<>(1, 0, 0)},
182
183 {"joint6", "link5", "link6", false, ChVector<>(0.00000, 0.00000, 0.00000), ChVector<>(0.00000, 0.00000, 0.00000),
184 ChVector<>(0, -1, 0)},
185
186 {"ftadapter_joint", "link6", "ftadapter_link", true, ChVector<>(0.20739, -0.12100, 0.00000),
187 ChVector<>(0.00000, 0.00000, 0.00000), ChVector<>(1, 0, 0)},
188
189 {"ft_joint", "ftadapter_link", "ft_link", true, ChVector<>(0.0263755, 0.00000, 0.00000),
190 ChVector<>(0.00000, 0.00000, 0.00000), ChVector<>(1, 0, 0)},
191
192 {"joint7", "link6", "link7", false, ChVector<>(0.19250, -0.11700, 0.00000), ChVector<>(0.00000, 0.00000, 0.00000),
193 ChVector<>(1, 0, 0)},
194
195 {"joint8", "link7", "link8", false, ChVector<>(0.12024, 0.17200, 0.00000), ChVector<>(-1.57000, 0.00000, 0.00000),
196 ChVector<>(0, 0, 1)}
197
198 };
199
200 // =============================================================================
201
202 // Convert a triplet (roll-pitch-yaw) to a quaternion
rpy2quat(const ChVector<> & rpy)203 ChQuaternion<> rpy2quat(const ChVector<>& rpy) {
204 return Q_from_AngZ(rpy.z()) * Q_from_AngY(rpy.y()) * Q_from_AngX(rpy.x());
205 }
206
207 // =============================================================================
208
209 // Calculate a coordinate system with the Z direction along 'axis' (given in 'base').
210 // Implicit assumption: 'axis' is always along X, Y, or Z.
calcJointFrame(const ChFrame<> & base,const ChVector<> & axis)211 ChCoordsys<> calcJointFrame(const ChFrame<>& base, const ChVector<>& axis) {
212 ChVector<> u;
213 ChVector<> v;
214 ChVector<> w = axis;
215 if (std::abs(axis.x()) > 0.5) {
216 v = ChVector<>(0, 1, 0);
217 u = Vcross(v, w);
218 } else {
219 u = ChVector<>(1, 0, 0);
220 v = Vcross(w, u);
221 }
222 ChMatrix33<> A;
223 A.Set_A_axis(u, v, w);
224 ChMatrix33<> B = base.GetA() * A;
225 return ChCoordsys<>(base.GetPos(), B.Get_A_quaternion());
226 }
227
228 // =============================================================================
229
230 // Convert the specified inertia properties into the centroidal reference frame.
231 // It is assumed that the centroidal frame is parallel with the reference frame.
232 class InertiaConverter {
233 public:
InertiaConverter(double mass,const ChVector<> & com,const ChVector<> & inertia_xx,const ChVector<> & inertia_xy)234 InertiaConverter(double mass, const ChVector<>& com, const ChVector<>& inertia_xx, const ChVector<>& inertia_xy) {
235 // Inertia matrix (wrt reference frame)
236 ChMatrix33<> J(inertia_xx, inertia_xy);
237
238 // Convert inertia to centroidal frame (parallel-axis theorem, no rotation)
239 ChVector<> diag(com.y() * com.y() + com.z() * com.z(), //
240 com.x() * com.x() + com.z() * com.z(), //
241 com.x() * com.x() + com.y() * com.y());
242 ChVector<> off_diag(-com.x() * com.y(), //
243 -com.x() * com.z(), //
244 -com.y() * com.z());
245 ChMatrix33<> offset(diag, off_diag);
246
247 ChMatrix33<> Jc = J - offset * mass;
248
249 // Extract centroidal moments and products of inertia
250 m_inertia_xx.x() = Jc(0, 0);
251 m_inertia_xx.y() = Jc(1, 1);
252 m_inertia_xx.z() = Jc(2, 2);
253
254 m_inertia_xy.x() = Jc(0, 1);
255 m_inertia_xy.y() = Jc(0, 2);
256 m_inertia_xy.z() = Jc(1, 2);
257
258 /*
259 std::cout << mass << // mass
260 " " << com.x() << " " << com.y() << " " << com.z() << // COM offset
261 " " << inertia_xx.x() << " " << inertia_xx.y() << " " << inertia_xx.z() << // moments (reference frame)
262 " " << inertia_xy.x() << " " << inertia_xy.y() << " " << inertia_xy.z() << // products (reference frame)
263 " " << m_inertia_xx.x() << " " << m_inertia_xx.y() << " " << m_inertia_xx.z() << // moments (centroidal)
264 " " << m_inertia_xy.x() << " " << m_inertia_xy.y() << " " << m_inertia_xy.z() << // products (centroidal)
265 std::endl;
266 */
267 }
268
269 ChVector<> m_inertia_xx; ///< moments of inertia (centroidal)
270 ChVector<> m_inertia_xy; ///< products of inertia (centroidal)
271 };
272
273 // =============================================================================
274
275 class ContactManager : public ChContactContainer::ReportContactCallback {
276 public:
277 ContactManager();
278 void Process(RoboSimian* robot);
279
280 private:
281 virtual bool OnReportContact(const ChVector<>& pA,
282 const ChVector<>& pB,
283 const ChMatrix33<>& plane_coord,
284 const double& distance,
285 const double& eff_radius,
286 const ChVector<>& react_forces,
287 const ChVector<>& react_torques,
288 ChContactable* modA,
289 ChContactable* modB) override;
290
291 int m_num_contacts;
292 };
293
ContactManager()294 ContactManager::ContactManager() {}
295
Process(RoboSimian * robot)296 void ContactManager::Process(RoboSimian* robot) {
297 std::cout << "Report contacts" << std::endl;
298 m_num_contacts = 0;
299 std::shared_ptr<ContactManager> shared_this(this, [](ContactManager*) {});
300 robot->GetSystem()->GetContactContainer()->ReportAllContacts(shared_this);
301 std::cout << " total actual contacts: " << m_num_contacts << std::endl << std::endl;
302 }
303
OnReportContact(const ChVector<> & pA,const ChVector<> & pB,const ChMatrix33<> & plane_coord,const double & distance,const double & eff_radius,const ChVector<> & react_forces,const ChVector<> & react_torques,ChContactable * modA,ChContactable * modB)304 bool ContactManager::OnReportContact(const ChVector<>& pA,
305 const ChVector<>& pB,
306 const ChMatrix33<>& plane_coord,
307 const double& distance,
308 const double& eff_radius,
309 const ChVector<>& react_forces,
310 const ChVector<>& react_torques,
311 ChContactable* modA,
312 ChContactable* modB) {
313 // Only report contacts with negative penetration (i.e. actual contacts).
314 if (distance >= 0)
315 return true;
316
317 auto bodyA = dynamic_cast<ChBodyAuxRef*>(modA);
318 auto bodyB = dynamic_cast<ChBodyAuxRef*>(modB);
319
320 // Filter robot bodies based on their IDs.
321 bool a = (bodyA && bodyA->GetId() < 100);
322 bool b = (bodyB && bodyB->GetId() < 100);
323
324 if (!a && !b)
325 return true;
326
327 std::cout << " " << (a ? bodyA->GetNameString() : "other") << " - " << (b ? bodyB->GetNameString() : "other")
328 << std::endl;
329
330 m_num_contacts++;
331
332 return true;
333 }
334
335 // =============================================================================
336
337 // Callback class for modifying composite material properties.
338 // Notes:
339 // - currently, only ChSystemMulticoreNSC support user-provided callbacks for overwriting composite material
340 // properties
341 // - as such, this functor class is only created when using NSC frictional contact
342 // - composite material properties are modified only for contacts involving the sled or one of the wheels
343 // - in these cases, the friction coefficient is set to the user-specified value
344 // - cohesion, restitution, and compliance are set to 0
345
346 class ContactMaterial : public ChContactContainer::AddContactCallback {
347 public:
ContactMaterial(RoboSimian * robot)348 ContactMaterial(RoboSimian* robot) : m_robot(robot) {
349 std::shared_ptr<ContactMaterial> shared_this(this, [](ContactMaterial*) {});
350 m_robot->GetSystem()->GetContactContainer()->RegisterAddContactCallback(shared_this);
351 }
352
OnAddContact(const collision::ChCollisionInfo & contactinfo,ChMaterialComposite * const material)353 virtual void OnAddContact(const collision::ChCollisionInfo& contactinfo,
354 ChMaterialComposite* const material) override {
355 //// TODO: currently, only NSC multicore systems support user override of composite materials.
356 auto mat = static_cast<ChMaterialCompositeNSC* const>(material);
357
358 // Contactables in current collision pair
359 auto contactableA = contactinfo.modelA->GetContactable();
360 auto contactableB = contactinfo.modelB->GetContactable();
361
362 // Overwrite composite material properties if collision involves the sled body
363 auto sled = m_robot->GetSledBody().get();
364 if (contactableA == sled || contactableB == sled) {
365 mat->static_friction = m_robot->m_sled_friction;
366 mat->sliding_friction = m_robot->m_sled_friction;
367 mat->cohesion = 0;
368 mat->restitution = 0;
369 mat->compliance = 0;
370 return;
371 }
372
373 // Overwrite composite material properties if collision involves a wheel body
374 auto wheel0 = m_robot->GetWheelBody(FR).get();
375 auto wheel1 = m_robot->GetWheelBody(FL).get();
376 auto wheel2 = m_robot->GetWheelBody(RR).get();
377 auto wheel3 = m_robot->GetWheelBody(RL).get();
378 if (contactableA == wheel0 || contactableB == wheel0 || contactableA == wheel1 || contactableB == wheel1 ||
379 contactableA == wheel2 || contactableB == wheel2 || contactableA == wheel3 || contactableB == wheel3) {
380 mat->static_friction = m_robot->m_wheel_friction;
381 mat->sliding_friction = m_robot->m_wheel_friction;
382 mat->cohesion = 0;
383 mat->restitution = 0;
384 mat->compliance = 0;
385 return;
386 }
387 }
388
389 private:
390 RoboSimian* m_robot;
391 };
392
393 // =============================================================================
394
RoboSimian(ChContactMethod contact_method,bool has_sled,bool fixed)395 RoboSimian::RoboSimian(ChContactMethod contact_method, bool has_sled, bool fixed)
396 : m_owns_system(true),
397 m_wheel_mode(ActuationMode::SPEED),
398 m_contact_reporter(new ContactManager),
399 m_material_override(nullptr),
400 m_sled_friction(0.8f),
401 m_wheel_friction(0.8f),
402 m_outdir(""),
403 m_root("results") {
404 m_system = (contact_method == ChContactMethod::NSC) ? static_cast<ChSystem*>(new ChSystemNSC)
405 : static_cast<ChSystem*>(new ChSystemSMC);
406 m_system->Set_G_acc(ChVector<>(0, 0, -9.81));
407
408 // Integration and Solver settings
409 m_system->SetSolverMaxIterations(150);
410 m_system->SetMaxPenetrationRecoverySpeed(4.0);
411 m_system->SetSolverType(ChSolver::Type::BARZILAIBORWEIN);
412
413 Create(has_sled, fixed);
414
415 //// TODO: currently, only NSC multicore systems support user override of composite materials
416 if (contact_method == ChContactMethod::NSC) {
417 m_material_override = new ContactMaterial(this);
418 }
419 }
420
RoboSimian(ChSystem * system,bool has_sled,bool fixed)421 RoboSimian::RoboSimian(ChSystem* system, bool has_sled, bool fixed)
422 : m_owns_system(false),
423 m_system(system),
424 m_wheel_mode(ActuationMode::SPEED),
425 m_contact_reporter(new ContactManager),
426 m_material_override(nullptr),
427 m_sled_friction(0.8f),
428 m_wheel_friction(0.8f),
429 m_outdir(""),
430 m_root("results") {
431 Create(has_sled, fixed);
432
433 //// TODO: currently, only NSC multicore systems support user override of composite materials
434 if (system->GetContactMethod() == ChContactMethod::NSC) {
435 m_material_override = new ContactMaterial(this);
436 }
437 }
438
~RoboSimian()439 RoboSimian::~RoboSimian() {
440 if (m_owns_system)
441 delete m_system;
442 delete m_contact_reporter;
443 delete m_material_override;
444 }
445
DefaultContactMaterial(ChContactMethod contact_method)446 std::shared_ptr<ChMaterialSurface> DefaultContactMaterial(ChContactMethod contact_method) {
447 float mu = 0.8f; // coefficient of friction
448 float cr = 0.0f; // coefficient of restitution
449 float Y = 2e7f; // Young's modulus
450 float nu = 0.3f; // Poisson ratio
451 float kn = 2e5f; // normal stiffness
452 float gn = 40.0f; // normal viscous damping
453 float kt = 2e5f; // tangential stiffness
454 float gt = 20.0f; // tangential viscous damping
455
456 switch (contact_method) {
457 case ChContactMethod::NSC: {
458 auto matNSC = chrono_types::make_shared<ChMaterialSurfaceNSC>();
459 matNSC->SetFriction(mu);
460 matNSC->SetRestitution(cr);
461 return matNSC;
462 }
463 case ChContactMethod::SMC: {
464 auto matSMC = chrono_types::make_shared<ChMaterialSurfaceSMC>();
465 matSMC->SetFriction(mu);
466 matSMC->SetRestitution(cr);
467 matSMC->SetYoungModulus(Y);
468 matSMC->SetPoissonRatio(nu);
469 matSMC->SetKn(kn);
470 matSMC->SetGn(gn);
471 matSMC->SetKt(kt);
472 matSMC->SetGt(gt);
473 return matSMC;
474 }
475 default:
476 return std::shared_ptr<ChMaterialSurface>();
477 }
478 }
479
Create(bool has_sled,bool fixed)480 void RoboSimian::Create(bool has_sled, bool fixed) {
481 auto contact_method = m_system->GetContactMethod();
482
483 // Set default collision model envelope commensurate with model dimensions.
484 // Note that an SMC system automatically sets envelope to 0.
485 if (contact_method == ChContactMethod::NSC) {
486 collision::ChCollisionModel::SetDefaultSuggestedEnvelope(0.01);
487 collision::ChCollisionModel::SetDefaultSuggestedMargin(0.005);
488 }
489
490 // Create the contact materials (all with default properties)
491 m_chassis_material = DefaultContactMaterial(contact_method);
492 m_sled_material = DefaultContactMaterial(contact_method);
493 m_wheel_material = DefaultContactMaterial(contact_method);
494 m_link_material = DefaultContactMaterial(contact_method);
495 m_wheelDD_material = DefaultContactMaterial(contact_method);
496
497 m_chassis = chrono_types::make_shared<RS_Chassis>("chassis", fixed, m_chassis_material, m_system);
498
499 if (has_sled)
500 m_sled = chrono_types::make_shared<RS_Sled>("sled", m_sled_material, m_system);
501
502 m_limbs.push_back(
503 chrono_types::make_shared<RS_Limb>("limb1", FR, front_links, m_wheel_material, m_link_material, m_system));
504 m_limbs.push_back(
505 chrono_types::make_shared<RS_Limb>("limb2", RR, rear_links, m_wheel_material, m_link_material, m_system));
506 m_limbs.push_back(
507 chrono_types::make_shared<RS_Limb>("limb3", RL, rear_links, m_wheel_material, m_link_material, m_system));
508 m_limbs.push_back(
509 chrono_types::make_shared<RS_Limb>("limb4", FL, front_links, m_wheel_material, m_link_material, m_system));
510
511 // The differential-drive wheels will be removed from robosimian
512 ////m_wheel_left = chrono_types::make_shared<RS_WheelDD>("dd_wheel_left", 2, m_wheelDD_material, m_system);
513 ////m_wheel_right = chrono_types::make_shared<RS_WheelDD>("dd_wheel_right", 3, m_wheelDD_material, m_system);
514
515 // Default visualization: COLLISION shapes
516 SetVisualizationTypeChassis(VisualizationType::COLLISION);
517 SetVisualizationTypeSled(VisualizationType::COLLISION);
518 SetVisualizationTypeLimbs(VisualizationType::COLLISION);
519 SetVisualizationTypeWheels(VisualizationType::COLLISION);
520 }
521
Initialize(const ChCoordsys<> & pos)522 void RoboSimian::Initialize(const ChCoordsys<>& pos) {
523 m_chassis->Initialize(pos);
524
525 if (m_sled)
526 m_sled->Initialize(m_chassis->m_body, ChVector<>(0.0, 0.0, 0.21), ChVector<>(1.570796, 0, 0));
527
528 m_limbs[FR]->Initialize(m_chassis->m_body, ChVector<>(+0.29326, +0.20940, 0.03650),
529 ChVector<>(0.00000, -1.57080, -0.26180), CollisionFamily::LIMB_FR, m_wheel_mode);
530 m_limbs[RR]->Initialize(m_chassis->m_body, ChVector<>(-0.29326, +0.20940, 0.03650),
531 ChVector<>(0.00000, -1.57080, +0.26180), CollisionFamily::LIMB_RR, m_wheel_mode);
532 m_limbs[RL]->Initialize(m_chassis->m_body, ChVector<>(-0.29326, -0.20940, 0.03650),
533 ChVector<>(0.00000, -1.57080, 2.87979), CollisionFamily::LIMB_RL, m_wheel_mode);
534 m_limbs[FL]->Initialize(m_chassis->m_body, ChVector<>(+0.29326, -0.20940, 0.03650),
535 ChVector<>(0.00000, -1.57080, 3.40339), CollisionFamily::LIMB_FL, m_wheel_mode);
536
537 ////m_wheel_left->Initialize(m_chassis->m_body, ChVector<>(-0.42943, -0.19252, 0.06380),
538 //// ChVector<>(0.00000, +1.57080, -1.57080));
539 ////m_wheel_right->Initialize(m_chassis->m_body, ChVector<>(-0.42943, +0.19252, 0.06380),
540 //// ChVector<>(0.00000, -1.57080, -1.57080));
541
542 // Create output files
543 for (int i = 0; i < 4; i++) {
544 m_outf[i].open(m_outdir + "/" + m_root + "_limb" + std::to_string(i) + ".dat");
545 m_outf[i].precision(7);
546 m_outf[i] << std::scientific;
547 }
548 }
549
SetCollide(int flags)550 void RoboSimian::SetCollide(int flags) {
551 m_chassis->SetCollide((flags & static_cast<int>(CollisionFlags::CHASSIS)) != 0);
552
553 if (m_sled)
554 m_sled->SetCollide((flags & static_cast<int>(CollisionFlags::SLED)) != 0);
555
556 for (auto limb : m_limbs) {
557 limb->SetCollideLinks((flags & static_cast<int>(CollisionFlags::LIMBS)) != 0);
558 limb->SetCollideWheel((flags & static_cast<int>(CollisionFlags::WHEELS)) != 0);
559 }
560 }
561
SetFrictionCoefficients(float sled_friction,float wheel_friction)562 void RoboSimian::SetFrictionCoefficients(float sled_friction, float wheel_friction) {
563 m_sled_friction = sled_friction;
564 m_wheel_friction = wheel_friction;
565 }
566
SetVisualizationTypeChassis(VisualizationType vis)567 void RoboSimian::SetVisualizationTypeChassis(VisualizationType vis) {
568 m_chassis->SetVisualizationType(vis);
569 }
570
SetVisualizationTypeSled(VisualizationType vis)571 void RoboSimian::SetVisualizationTypeSled(VisualizationType vis) {
572 if (m_sled)
573 m_sled->SetVisualizationType(vis);
574 }
575
SetVisualizationTypeLimb(LimbID id,VisualizationType vis)576 void RoboSimian::SetVisualizationTypeLimb(LimbID id, VisualizationType vis) {
577 m_limbs[id]->SetVisualizationType(vis);
578 }
579
SetVisualizationTypeLimbs(VisualizationType vis)580 void RoboSimian::SetVisualizationTypeLimbs(VisualizationType vis) {
581 for (auto limb : m_limbs)
582 limb->SetVisualizationType(vis);
583 }
584
SetVisualizationTypeWheels(VisualizationType vis)585 void RoboSimian::SetVisualizationTypeWheels(VisualizationType vis) {
586 ////m_wheel_left->SetVisualizationType(vis);
587 ////m_wheel_right->SetVisualizationType(vis);
588 }
589
SetDriver(std::shared_ptr<RS_Driver> driver)590 void RoboSimian::SetDriver(std::shared_ptr<RS_Driver> driver) {
591 m_driver = driver;
592 }
593
SetOutputDirectory(const std::string & outdir,const std::string & root)594 void RoboSimian::SetOutputDirectory(const std::string& outdir, const std::string& root) {
595 m_outdir = outdir;
596 m_root = root;
597 }
598
Activate(LimbID id,const std::string & motor_name,double time,double val)599 void RoboSimian::Activate(LimbID id, const std::string& motor_name, double time, double val) {
600 m_limbs[id]->Activate(motor_name, time, val);
601 }
602
DoStepDynamics(double step)603 void RoboSimian::DoStepDynamics(double step) {
604 static double w[4] = {0, 0, 0, 0};
605
606 if (m_driver) {
607 // Update driver
608 double time = m_system->GetChTime();
609 m_driver->Update(time);
610
611 // Get driver activations
612 Actuation actuation = m_driver->GetActuation();
613
614 if (m_wheel_mode == ActuationMode::ANGLE) {
615 // Overwrite wheel actuations (angle instead of speed)
616 for (int i = 0; i < 4; i++) {
617 double speed = actuation[i][7];
618 double pos = w[i] + speed * step;
619 actuation[i][7] = pos;
620 w[i] = pos;
621 }
622 }
623
624 // Apply activations to limbs
625 for (int i = 0; i < 4; i++)
626 m_limbs[i]->Activate(time, actuation[i]);
627 }
628
629 // Advance system state
630 m_system->DoStepDynamics(step);
631 }
632
Translate(const ChVector<> & shift)633 void RoboSimian::Translate(const ChVector<>& shift) {
634 m_chassis->Translate(shift);
635 if (m_sled)
636 m_sled->Translate(shift);
637 m_limbs[FR]->Translate(shift);
638 m_limbs[RR]->Translate(shift);
639 m_limbs[RL]->Translate(shift);
640 m_limbs[FL]->Translate(shift);
641 }
642
ReportContacts()643 void RoboSimian::ReportContacts() {
644 m_contact_reporter->Process(this);
645 }
646
Output()647 void RoboSimian::Output() {
648 for (int i = 0; i < 4; i++) {
649 // Current motor angles
650 m_outf[i] << m_system->GetChTime();
651 std::array<double, 8> angles = m_limbs[i]->GetMotorAngles();
652 for (auto v : angles) {
653 m_outf[i] << " " << v;
654 }
655 // Current motor angular speeds
656 std::array<double, 8> speeds = m_limbs[i]->GetMotorOmegas();
657 for (auto v : speeds) {
658 m_outf[i] << " " << v;
659 }
660 // Current motor (reaction) torques
661 std::array<double, 8> torques = m_limbs[i]->GetMotorTorques();
662 for (auto v : torques) {
663 m_outf[i] << " " << v;
664 }
665 // Actuations data
666 m_limbs[i]->GetMotorActuations(angles, speeds);
667 for (auto v : angles) {
668 m_outf[i] << " " << v;
669 }
670 for (auto v : speeds) {
671 m_outf[i] << " " << v;
672 }
673 m_outf[i] << std::endl;
674 }
675 }
676
GetMass() const677 double RoboSimian::GetMass() const {
678 return GetChassisBody()->GetMass() + GetSledBody()->GetMass() + //
679 m_limbs[FR]->GetMass() + m_limbs[FL]->GetMass() + //
680 m_limbs[RR]->GetMass() + m_limbs[RL]->GetMass();
681 }
682
683 // =============================================================================
684
685 class ax {
686 public:
operator ()(const double & v)687 double operator()(const double& v) { return a * v; }
688 double a;
689 };
690
691 class axpby {
692 public:
operator ()(const double & v1,const double & v2)693 double operator()(const double& v1, const double& v2) { return a1 * v1 + a2 * v2; }
694 double a1;
695 double a2;
696 };
697
698 const std::string RS_Driver::m_phase_names[] = {"POSE", "HOLD", "START", "CYCLE", "STOP"};
699
RS_Driver(const std::string & filename_start,const std::string & filename_cycle,const std::string & filename_stop,bool repeat)700 RS_Driver::RS_Driver(const std::string& filename_start,
701 const std::string& filename_cycle,
702 const std::string& filename_stop,
703 bool repeat)
704 : m_repeat(repeat), m_time_pose(0), m_time_hold(0), m_offset(0), m_phase(POSE), m_callback(nullptr) {
705 assert(!filename_cycle.empty());
706 m_ifs_cycle.open(filename_cycle.c_str());
707
708 if (!filename_start.empty()) {
709 m_ifs_start.open(filename_start.c_str());
710 m_ifs = &m_ifs_start;
711 } else {
712 m_ifs = &m_ifs_cycle;
713 }
714
715 if (!filename_stop.empty()) {
716 m_ifs_stop.open(filename_stop.c_str());
717 }
718
719 LoadDataLine(m_time_1, m_actuations_1);
720 LoadDataLine(m_time_2, m_actuations_2);
721 }
722
~RS_Driver()723 RS_Driver::~RS_Driver() {}
724
SetTimeOffsets(double time_pose,double time_hold)725 void RS_Driver::SetTimeOffsets(double time_pose, double time_hold) {
726 m_time_pose = time_pose;
727 m_time_hold = time_hold;
728 m_offset = time_pose + time_hold;
729 }
730
LoadDataLine(double & time,Actuation & activations)731 void RS_Driver::LoadDataLine(double& time, Actuation& activations) {
732 *m_ifs >> time;
733 for (int i = 0; i < 4; i++) {
734 for (int j = 0; j < 8; j++) {
735 *m_ifs >> activations[i][j];
736 }
737 }
738 }
739
Update(double time)740 void RS_Driver::Update(double time) {
741 bool ext_actuation = false;
742 // In the POSE phase, use a logistic function to reach first data entry
743 if (m_phase == POSE) {
744 ax op;
745 double x = 20 * (time / m_time_pose) - 10;
746 op.a = std::exp(x) / (1 + std::exp(x));
747 for (int i = 0; i < 4; i++) {
748 std::transform(m_actuations_1[i].begin(), m_actuations_1[i].end(), m_actuations[i].begin(), op);
749 }
750 if (time >= m_time_pose) {
751 m_phase = HOLD;
752 std::cout << "time = " << time << " Switch to phase: " << GetCurrentPhase() << std::endl;
753 if (m_callback)
754 m_callback->OnPhaseChange(POSE, m_phase);
755 }
756 return;
757 }
758
759 // In the HOLD phase, always use the first data entry
760 if (m_phase == HOLD) {
761 m_actuations = m_actuations_1;
762 if (time >= m_offset) {
763 m_phase = (m_ifs_start.is_open()) ? START : CYCLE;
764 std::cout << "time = " << time << " Switch to phase: " << GetCurrentPhase() << std::endl;
765 if (m_callback)
766 m_callback->OnPhaseChange(HOLD, m_phase);
767 }
768 return;
769 }
770
771 // Offset time
772 double t = time - m_offset;
773
774 switch (m_phase) {
775 case START:
776 while (t > m_time_2) {
777 m_time_1 = m_time_2;
778 m_actuations_1 = m_actuations_2;
779 if (!m_ifs->eof()) {
780 LoadDataLine(m_time_2, m_actuations_2);
781 } else {
782 m_phase = CYCLE;
783 m_ifs = &m_ifs_cycle;
784 LoadDataLine(m_time_1, m_actuations_1);
785 LoadDataLine(m_time_2, m_actuations_2);
786 m_offset = time;
787 std::cout << "time = " << time << " Switch to phase: " << GetCurrentPhase() << std::endl;
788 if (m_callback)
789 m_callback->OnPhaseChange(START, CYCLE);
790 return;
791 }
792 }
793
794 break;
795
796 case CYCLE:
797 if (!driven) {
798 while (t > m_time_2) {
799 m_time_1 = m_time_2;
800 m_actuations_1 = m_actuations_2;
801 if (m_ifs->eof()) {
802 if (m_repeat) {
803 m_ifs->clear();
804 m_ifs->seekg(0);
805 LoadDataLine(m_time_1, m_actuations_1);
806 LoadDataLine(m_time_2, m_actuations_2);
807 m_offset = time;
808 std::cout << "time = " << time << " New cycle" << std::endl;
809 if (m_callback)
810 m_callback->OnPhaseChange(CYCLE, CYCLE);
811 }
812 return;
813 }
814 LoadDataLine(m_time_2, m_actuations_2);
815 }
816 } else {
817 ext_actuation = true;
818 }
819
820 break;
821
822 case STOP:
823 //// TODO
824 break;
825
826 default:
827 break;
828 }
829
830 // Interpolate v = alpha_1 * v_1 + alpha_2 * v_2
831 if (!ext_actuation) {
832 axpby op;
833 op.a1 = (t - m_time_2) / (m_time_1 - m_time_2);
834 op.a2 = (t - m_time_1) / (m_time_2 - m_time_1);
835 for (int i = 0; i < 4; i++) {
836 std::transform(m_actuations_1[i].begin(), m_actuations_1[i].end(), m_actuations_2[i].begin(),
837 m_actuations[i].begin(), op);
838 }
839 }
840 }
841
842 // =============================================================================
843
OnPhaseChange(RS_Driver::Phase old_phase,RS_Driver::Phase new_phase)844 void RS_DriverCallback::OnPhaseChange(RS_Driver::Phase old_phase, RS_Driver::Phase new_phase) {
845 if (new_phase == RS_Driver::HOLD) {
846 auto& fl = m_robot->GetWheelPos(FL);
847 auto& fr = m_robot->GetWheelPos(FR);
848 auto& rl = m_robot->GetWheelPos(RL);
849 auto& rr = m_robot->GetWheelPos(RR);
850 std::cout << " wheel FL: " << fl.x() << " " << fl.y() << std::endl;
851 std::cout << " wheel FR: " << fr.x() << " " << fr.y() << std::endl;
852 std::cout << " wheel RL: " << rl.x() << " " << rl.y() << std::endl;
853 std::cout << " wheel RR: " << rr.x() << " " << rr.y() << std::endl;
854 }
855 if (new_phase == RS_Driver::CYCLE && old_phase != RS_Driver::CYCLE) {
856 m_start_x = m_robot->GetChassisPos().x();
857 m_start_time = m_robot->GetSystem()->GetChTime();
858 }
859 }
860
861 // =============================================================================
862
RS_Part(const std::string & name,std::shared_ptr<ChMaterialSurface> mat,ChSystem * system)863 RS_Part::RS_Part(const std::string& name, std::shared_ptr<ChMaterialSurface> mat, ChSystem* system)
864 : m_name(name), m_mat(mat) {
865 m_body = std::shared_ptr<ChBodyAuxRef>(system->NewBodyAuxRef());
866 m_body->SetNameString(name + "_body");
867 }
868
SetVisualizationType(VisualizationType vis)869 void RS_Part::SetVisualizationType(VisualizationType vis) {
870 m_body->GetAssets().clear();
871 AddVisualizationAssets(vis);
872 }
873
AddVisualizationAssets(VisualizationType vis)874 void RS_Part::AddVisualizationAssets(VisualizationType vis) {
875 if (vis == VisualizationType::NONE)
876 return;
877
878 auto col = chrono_types::make_shared<ChColorAsset>();
879 col->SetColor(m_color);
880 m_body->AddAsset(col);
881
882 if (vis == VisualizationType::MESH) {
883 std::string vis_mesh_file = "robot/robosimian/obj/" + m_mesh_name + ".obj";
884 auto trimesh = chrono_types::make_shared<geometry::ChTriangleMeshConnected>();
885 trimesh->LoadWavefrontMesh(GetChronoDataFile(vis_mesh_file), true, false);
886 //// HACK: a trimesh visual asset ignores transforms! Explicitly offset vertices.
887 trimesh->Transform(m_offset, ChMatrix33<>(1));
888 auto trimesh_shape = chrono_types::make_shared<ChTriangleMeshShape>();
889 trimesh_shape->SetMesh(trimesh);
890 trimesh_shape->SetName(m_mesh_name);
891 ////trimesh_shape->Pos = m_offset;
892 trimesh_shape->SetStatic(true);
893 m_body->AddAsset(trimesh_shape);
894 return;
895 }
896
897 for (auto box : m_boxes) {
898 auto box_shape = chrono_types::make_shared<ChBoxShape>();
899 box_shape->GetBoxGeometry().SetLengths(box.m_dims);
900 box_shape->Pos = box.m_pos;
901 box_shape->Rot = box.m_rot;
902 m_body->AddAsset(box_shape);
903 }
904
905 for (auto cyl : m_cylinders) {
906 //// HACK: Chrono::OpenGL does not properly account for Pos & Rot.
907 //// So transform the end points explicitly.
908 ChCoordsys<> csys(cyl.m_pos, cyl.m_rot);
909 ChVector<> p1 = csys * ChVector<>(0, cyl.m_length / 2, 0);
910 ChVector<> p2 = csys * ChVector<>(0, -cyl.m_length / 2, 0);
911 auto cyl_shape = chrono_types::make_shared<ChCylinderShape>();
912 cyl_shape->GetCylinderGeometry().rad = cyl.m_radius;
913 cyl_shape->GetCylinderGeometry().p1 = p1;
914 cyl_shape->GetCylinderGeometry().p2 = p2;
915 m_body->AddAsset(cyl_shape);
916 }
917
918 for (auto sphere : m_spheres) {
919 auto sphere_shape = chrono_types::make_shared<ChSphereShape>();
920 sphere_shape->GetSphereGeometry().rad = sphere.m_radius;
921 sphere_shape->Pos = sphere.m_pos;
922 m_body->AddAsset(sphere_shape);
923 }
924
925 for (auto mesh : m_meshes) {
926 std::string vis_mesh_file = "robot/robosimian/obj/" + mesh.m_name + ".obj";
927 auto trimesh = chrono_types::make_shared<geometry::ChTriangleMeshConnected>();
928 trimesh->LoadWavefrontMesh(GetChronoDataFile(vis_mesh_file), true, false);
929 //// HACK: a trimesh visual asset ignores transforms! Explicitly offset vertices.
930 trimesh->Transform(mesh.m_pos, ChMatrix33<>(mesh.m_rot));
931 auto trimesh_shape = chrono_types::make_shared<ChTriangleMeshShape>();
932 trimesh_shape->SetMesh(trimesh);
933 trimesh_shape->SetName(mesh.m_name);
934 ////trimesh_shape->Pos = m_offset;
935 trimesh_shape->SetStatic(true);
936 m_body->AddAsset(trimesh_shape);
937 }
938 }
939
AddCollisionShapes()940 void RS_Part::AddCollisionShapes() {
941 m_body->GetCollisionModel()->ClearModel();
942
943 for (const auto& sphere : m_spheres) {
944 m_body->GetCollisionModel()->AddSphere(m_mat, sphere.m_radius, sphere.m_pos);
945 }
946 for (const auto& box : m_boxes) {
947 ChVector<> hdims = box.m_dims / 2;
948 m_body->GetCollisionModel()->AddBox(m_mat, hdims.x(), hdims.y(), hdims.z(), box.m_pos, box.m_rot);
949 }
950 for (const auto& cyl : m_cylinders) {
951 m_body->GetCollisionModel()->AddCylinder(m_mat, cyl.m_radius, cyl.m_radius, cyl.m_length / 2, cyl.m_pos,
952 cyl.m_rot);
953 }
954 for (auto mesh : m_meshes) {
955 std::string vis_mesh_file = "robot/robosimian/obj/" + mesh.m_name + ".obj";
956 auto trimesh = chrono_types::make_shared<geometry::ChTriangleMeshConnected>();
957 trimesh->LoadWavefrontMesh(GetChronoDataFile(vis_mesh_file), false, false);
958 switch (mesh.m_type) {
959 case MeshShape::Type::CONVEX_HULL:
960 m_body->GetCollisionModel()->AddConvexHull(m_mat, trimesh->getCoordsVertices(), mesh.m_pos, mesh.m_rot);
961 break;
962 case MeshShape::Type::TRIANGLE_SOUP:
963 m_body->GetCollisionModel()->AddTriangleMesh(m_mat, trimesh, false, false, mesh.m_pos, mesh.m_rot,
964 0.002);
965 break;
966 case MeshShape::Type::NODE_CLOUD:
967 for (const auto& v : trimesh->getCoordsVertices()) {
968 m_body->GetCollisionModel()->AddSphere(m_mat, 0.002, v);
969 }
970 break;
971 }
972 }
973
974 m_body->GetCollisionModel()->BuildModel();
975 }
976
977 // =============================================================================
978
RS_Chassis(const std::string & name,bool fixed,std::shared_ptr<ChMaterialSurface> mat,ChSystem * system)979 RS_Chassis::RS_Chassis(const std::string& name, bool fixed, std::shared_ptr<ChMaterialSurface> mat, ChSystem* system)
980 : RS_Part(name, mat, system), m_collide(false) {
981 double mass = 46.658335;
982 ChVector<> com(0.040288, -0.001937, -0.073574);
983 ChVector<> inertia_xx(1.272134, 2.568776, 3.086984);
984 ChVector<> inertia_xy(0.008890, -0.13942, 0.000325);
985
986 m_body->SetIdentifier(0);
987 m_body->SetMass(mass);
988 m_body->SetFrame_COG_to_REF(ChFrame<>(com, ChQuaternion<>(1, 0, 0, 0)));
989 m_body->SetInertiaXX(inertia_xx);
990 m_body->SetInertiaXY(inertia_xy);
991 m_body->SetBodyFixed(fixed);
992 system->Add(m_body);
993
994 // Create the set of primitive shapes
995 m_boxes.push_back(BoxShape(VNULL, QUNIT, ChVector<>(0.257, 0.50, 0.238)));
996 m_boxes.push_back(BoxShape(VNULL, QUNIT, ChVector<>(0.93, 0.230, 0.238)));
997 m_boxes.push_back(
998 BoxShape(ChVector<>(+0.25393, +0.075769, 0), Q_from_AngZ(-0.38153), ChVector<>(0.36257, 0.23, 0.238)));
999 m_boxes.push_back(
1000 BoxShape(ChVector<>(-0.25393, +0.075769, 0), Q_from_AngZ(+0.38153), ChVector<>(0.36257, 0.23, 0.238)));
1001 m_boxes.push_back(
1002 BoxShape(ChVector<>(+0.25393, -0.075769, 0), Q_from_AngZ(+0.38153), ChVector<>(0.36257, 0.23, 0.238)));
1003 m_boxes.push_back(
1004 BoxShape(ChVector<>(-0.25393, -0.075769, 0), Q_from_AngZ(-0.38153), ChVector<>(0.36257, 0.23, 0.238)));
1005
1006 m_cylinders.push_back(CylinderShape(ChVector<>(0.417050, 0, -0.158640),
1007 Q_from_AngZ(CH_C_PI_2) * Q_from_AngX(CH_C_PI_2 - 0.383972), 0.05, 0.144));
1008
1009 // Geometry for link0 (all limbs); these links are fixed to the chassis
1010 m_cylinders.push_back(
1011 CylinderShape(ChVector<>(+0.29326, +0.20940, 0.03650 - 0.025), Q_from_AngX(CH_C_PI_2), 0.05, 0.145));
1012 m_cylinders.push_back(
1013 CylinderShape(ChVector<>(-0.29326, +0.20940, 0.03650 - 0.025), Q_from_AngX(CH_C_PI_2), 0.05, 0.145));
1014 m_cylinders.push_back(
1015 CylinderShape(ChVector<>(-0.29326, -0.20940, 0.03650 - 0.025), Q_from_AngX(CH_C_PI_2), 0.05, 0.145));
1016 m_cylinders.push_back(
1017 CylinderShape(ChVector<>(+0.29326, -0.20940, 0.03650 - 0.025), Q_from_AngX(CH_C_PI_2), 0.05, 0.145));
1018
1019 // Set the name of the visualization mesh
1020 m_mesh_name = "robosim_chassis";
1021 m_offset = ChVector<>(0, 0, 0);
1022 m_color = ChColor(0.4f, 0.4f, 0.7f);
1023 }
1024
Initialize(const ChCoordsys<> & pos)1025 void RS_Chassis::Initialize(const ChCoordsys<>& pos) {
1026 m_body->SetFrame_REF_to_abs(ChFrame<>(pos));
1027
1028 AddCollisionShapes();
1029
1030 m_body->GetCollisionModel()->SetFamily(CollisionFamily::CHASSIS);
1031 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::LIMB_FR);
1032 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::LIMB_RR);
1033 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::LIMB_RL);
1034 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::LIMB_FL);
1035 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::SLED);
1036
1037 // Note: call this AFTER setting the collision family (required for Chrono::Multicore)
1038 m_body->SetCollide(m_collide);
1039 }
1040
SetCollide(bool state)1041 void RS_Chassis::SetCollide(bool state) {
1042 m_collide = state;
1043 m_body->SetCollide(state);
1044 }
1045
Translate(const ChVector<> & shift)1046 void RS_Chassis::Translate(const ChVector<>& shift) {
1047 m_body->SetPos(m_body->GetPos() + shift);
1048 }
1049
1050 // =============================================================================
1051
RS_Sled(const std::string & name,std::shared_ptr<ChMaterialSurface> mat,ChSystem * system)1052 RS_Sled::RS_Sled(const std::string& name, std::shared_ptr<ChMaterialSurface> mat, ChSystem* system)
1053 : RS_Part(name, mat, system), m_collide(true) {
1054 double mass = 2.768775;
1055 ChVector<> com(0.000000, 0.000000, 0.146762);
1056 ChVector<> inertia_xx(0.034856, 0.082427, 0.105853);
1057 ChVector<> inertia_xy(0.000007, -0.000002, 0);
1058
1059 m_body->SetIdentifier(1);
1060 m_body->SetMass(mass);
1061 m_body->SetFrame_COG_to_REF(ChFrame<>(com, ChQuaternion<>(1, 0, 0, 0)));
1062 m_body->SetInertiaXX(inertia_xx);
1063 m_body->SetInertiaXY(inertia_xy);
1064 system->Add(m_body);
1065
1066 m_meshes.push_back(MeshShape(ChVector<>(0, 0, 0), QUNIT, "robosim_sled_coll", MeshShape::Type::CONVEX_HULL));
1067
1068 m_mesh_name = "robosim_sled";
1069 m_offset = ChVector<>(0, 0, 0);
1070 m_color = ChColor(0.7f, 0.7f, 0.7f);
1071 }
1072
Initialize(std::shared_ptr<ChBodyAuxRef> chassis,const ChVector<> & xyz,const ChVector<> & rpy)1073 void RS_Sled::Initialize(std::shared_ptr<ChBodyAuxRef> chassis, const ChVector<>& xyz, const ChVector<>& rpy) {
1074 const ChFrame<>& X_GP = chassis->GetFrame_REF_to_abs(); // global -> parent
1075 ChFrame<> X_PC(xyz, rpy2quat(rpy)); // parent -> child
1076 ChFrame<> X_GC = X_GP * X_PC; // global -> child
1077 m_body->SetFrame_REF_to_abs(X_GC);
1078
1079 AddCollisionShapes();
1080
1081 m_body->GetCollisionModel()->SetFamily(CollisionFamily::SLED);
1082 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::LIMB_FR);
1083 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::LIMB_RR);
1084 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::LIMB_RL);
1085 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::LIMB_FL);
1086
1087 // Note: call this AFTER setting the collision family (required for Chrono::Multicore)
1088 m_body->SetCollide(m_collide);
1089
1090 // Add joint (weld)
1091 auto joint = chrono_types::make_shared<ChLinkLockLock>();
1092 joint->Initialize(chassis, m_body, calcJointFrame(X_GC, ChVector<>(1, 0, 0)));
1093 chassis->GetSystem()->AddLink(joint);
1094 }
1095
SetCollide(bool state)1096 void RS_Sled::SetCollide(bool state) {
1097 m_collide = state;
1098 m_body->SetCollide(state);
1099 }
1100
Translate(const ChVector<> & shift)1101 void RS_Sled::Translate(const ChVector<>& shift) {
1102 m_body->SetPos(m_body->GetPos() + shift);
1103 }
1104
1105 // =============================================================================
1106
RS_WheelDD(const std::string & name,int id,std::shared_ptr<ChMaterialSurface> mat,ChSystem * system)1107 RS_WheelDD::RS_WheelDD(const std::string& name,
1108 int id,
1109 std::shared_ptr<ChMaterialSurface> mat,
1110 ChSystem* system)
1111 : RS_Part(name, mat, system) {
1112 double mass = 3.492500;
1113 ChVector<> com(0, 0, 0);
1114 ChVector<> inertia_xx(0.01, 0.01, 0.02);
1115 ChVector<> inertia_xy(0, 0, 0);
1116
1117 m_body->SetIdentifier(id);
1118 m_body->SetMass(mass);
1119 m_body->SetFrame_COG_to_REF(ChFrame<>(com, ChQuaternion<>(1, 0, 0, 0)));
1120 m_body->SetInertiaXX(inertia_xx);
1121 m_body->SetInertiaXY(inertia_xy);
1122 system->Add(m_body);
1123
1124 m_cylinders.push_back(CylinderShape(ChVector<>(0, 0, 0), Q_from_AngX(CH_C_PI_2), 0.074, 0.038));
1125
1126 m_mesh_name = "robosim_dd_wheel";
1127 m_offset = ChVector<>(0, 0, 0);
1128 m_color = ChColor(0.3f, 0.3f, 0.3f);
1129 }
1130
Initialize(std::shared_ptr<ChBodyAuxRef> chassis,const ChVector<> & xyz,const ChVector<> & rpy)1131 void RS_WheelDD::Initialize(std::shared_ptr<ChBodyAuxRef> chassis, const ChVector<>& xyz, const ChVector<>& rpy) {
1132 const ChFrame<>& X_GP = chassis->GetFrame_REF_to_abs(); // global -> parent
1133 ChFrame<> X_PC(xyz, rpy2quat(rpy)); // parent -> child
1134 ChFrame<> X_GC = X_GP * X_PC; // global -> child
1135 m_body->SetFrame_REF_to_abs(X_GC);
1136
1137 AddCollisionShapes();
1138
1139 m_body->GetCollisionModel()->SetFamily(CollisionFamily::WHEEL_DD);
1140 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::CHASSIS);
1141 m_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(CollisionFamily::SLED);
1142
1143 // Note: call this AFTER setting the collision family (required for Chrono::Multicore)
1144 m_body->SetCollide(true);
1145
1146 // Add joint
1147 auto joint = chrono_types::make_shared<ChLinkLockRevolute>();
1148 joint->Initialize(chassis, m_body, calcJointFrame(X_GC, ChVector<>(0, 0, 1)));
1149 chassis->GetSystem()->AddLink(joint);
1150 }
1151
1152 // =============================================================================
1153
RS_Limb(const std::string & name,LimbID id,const LinkData data[],std::shared_ptr<ChMaterialSurface> wheel_mat,std::shared_ptr<ChMaterialSurface> link_mat,ChSystem * system)1154 RS_Limb::RS_Limb(const std::string& name,
1155 LimbID id,
1156 const LinkData data[],
1157 std::shared_ptr<ChMaterialSurface> wheel_mat,
1158 std::shared_ptr<ChMaterialSurface> link_mat,
1159 ChSystem* system)
1160 : m_name(name), m_collide_links(false), m_collide_wheel(true) {
1161 for (int i = 0; i < num_links; i++) {
1162 bool is_wheel = (data[i].name.compare("link8") == 0);
1163
1164 std::shared_ptr<RS_Part> link;
1165 if (is_wheel) {
1166 link = chrono_types::make_shared<RS_Part>(m_name + "_" + data[i].name, wheel_mat, system);
1167 } else {
1168 link = chrono_types::make_shared<RS_Part>(m_name + "_" + data[i].name, link_mat, system);
1169 }
1170
1171 double mass = data[i].link.m_mass;
1172 ChVector<> com = data[i].link.m_com;
1173 ChVector<> inertia_xx = data[i].link.m_inertia_xx;
1174 ChVector<> inertia_xy = data[i].link.m_inertia_xy;
1175
1176 link->m_body->SetIdentifier(4 + 4 * id + i);
1177 link->m_body->SetMass(mass);
1178 link->m_body->SetFrame_COG_to_REF(ChFrame<>(com, ChQuaternion<>(1, 0, 0, 0)));
1179 link->m_body->SetInertiaXX(inertia_xx);
1180 link->m_body->SetInertiaXY(inertia_xy);
1181
1182 link->m_mesh_name = data[i].link.m_mesh_name;
1183 link->m_offset = data[i].link.m_offset;
1184 link->m_color = data[i].link.m_color;
1185
1186 for (auto cyl : data[i].link.m_shapes) {
1187 link->m_cylinders.push_back(cyl);
1188 }
1189
1190 if (data[i].include)
1191 system->Add(link->m_body);
1192
1193 m_links.insert(std::make_pair(data[i].name, link));
1194 if (is_wheel)
1195 m_wheel = link;
1196 }
1197 }
1198
Initialize(std::shared_ptr<ChBodyAuxRef> chassis,const ChVector<> & xyz,const ChVector<> & rpy,CollisionFamily::Enum collision_family,ActuationMode wheel_mode)1199 void RS_Limb::Initialize(std::shared_ptr<ChBodyAuxRef> chassis,
1200 const ChVector<>& xyz,
1201 const ChVector<>& rpy,
1202 CollisionFamily::Enum collision_family,
1203 ActuationMode wheel_mode) {
1204 // Set absolute position of link0
1205 auto parent_body = chassis; // parent body
1206 auto child_body = m_links.find("link0")->second->m_body; // child body
1207 ChFrame<> X_GP = parent_body->GetFrame_REF_to_abs(); // global -> parent
1208 ChFrame<> X_PC(xyz, rpy2quat(rpy)); // parent -> child
1209 ChFrame<> X_GC = X_GP * X_PC; // global -> child
1210 child_body->SetFrame_REF_to_abs(X_GC);
1211
1212 // Traverse chain (base-to-tip)
1213 // set absolute position of the child body
1214 // add collision shapes on child body
1215 // create joint between parent and child
1216 for (int i = 0; i < num_joints; i++) {
1217 auto parent = m_links.find(joints[i].linkA)->second; // parent part
1218 auto child = m_links.find(joints[i].linkB)->second; // child part
1219 parent_body = parent->m_body; // parent body
1220 child_body = child->m_body; // child body
1221 X_GP = parent_body->GetFrame_REF_to_abs(); // global -> parent
1222 X_PC = ChFrame<>(joints[i].xyz, rpy2quat(joints[i].rpy)); // parent -> child
1223 X_GC = X_GP * X_PC; // global -> child
1224 child_body->SetFrame_REF_to_abs(X_GC);
1225
1226 // First joint connects directly to chassis
1227 if (i == 0)
1228 parent_body = chassis;
1229
1230 // Add contact geometry to child body
1231 child->AddCollisionShapes();
1232
1233 // Place all links from this limb in the same collision family
1234 child_body->GetCollisionModel()->SetFamily(collision_family);
1235 child_body->GetCollisionModel()->SetFamilyMaskNoCollisionWithFamily(collision_family);
1236
1237 // Note: call this AFTER setting the collision family (required for Chrono::Multicore)
1238 if (child == m_wheel)
1239 child_body->SetCollide(m_collide_wheel);
1240 else
1241 child_body->SetCollide(m_collide_links);
1242
1243 // Weld joints
1244 if (joints[i].fixed) {
1245 auto joint = chrono_types::make_shared<ChLinkLockLock>();
1246 joint->SetNameString(m_name + "_" + joints[i].name);
1247 joint->Initialize(parent_body, child_body, calcJointFrame(X_GC, joints[i].axis));
1248 chassis->GetSystem()->AddLink(joint);
1249 m_joints.insert(std::make_pair(joints[i].name, joint));
1250 continue;
1251 }
1252
1253 // Actuated joints (except the wheel motor joint)
1254 if (joints[i].name.compare("joint8") != 0) {
1255 auto motor_fun = chrono_types::make_shared<ChFunction_Setpoint>();
1256 auto joint = chrono_types::make_shared<ChLinkMotorRotationAngle>();
1257 joint->SetNameString(m_name + "_" + joints[i].name);
1258 joint->Initialize(parent_body, child_body, ChFrame<>(calcJointFrame(X_GC, joints[i].axis)));
1259 joint->SetAngleFunction(motor_fun);
1260 chassis->GetSystem()->AddLink(joint);
1261 m_joints.insert(std::make_pair(joints[i].name, joint));
1262 m_motors.insert(std::make_pair(joints[i].name, joint));
1263 continue;
1264 }
1265
1266 // Wheel motor joint
1267 if (wheel_mode == ActuationMode::SPEED) {
1268 auto motor_fun = chrono_types::make_shared<ChFunction_Setpoint>();
1269 auto joint = chrono_types::make_shared<ChLinkMotorRotationSpeed>();
1270 joint->SetNameString(m_name + "_" + joints[i].name);
1271 joint->Initialize(parent_body, child_body, ChFrame<>(calcJointFrame(X_GC, joints[i].axis)));
1272 joint->SetSpeedFunction(motor_fun);
1273 chassis->GetSystem()->AddLink(joint);
1274 m_joints.insert(std::make_pair(joints[i].name, joint));
1275 m_motors.insert(std::make_pair(joints[i].name, joint));
1276 m_wheel_motor = joint;
1277 } else {
1278 auto motor_fun = chrono_types::make_shared<ChFunction_Setpoint>();
1279 auto joint = chrono_types::make_shared<ChLinkMotorRotationAngle>();
1280 joint->SetNameString(m_name + "_" + joints[i].name);
1281 joint->Initialize(parent_body, child_body, ChFrame<>(calcJointFrame(X_GC, joints[i].axis)));
1282 joint->SetAngleFunction(motor_fun);
1283 chassis->GetSystem()->AddLink(joint);
1284 m_joints.insert(std::make_pair(joints[i].name, joint));
1285 m_motors.insert(std::make_pair(joints[i].name, joint));
1286 m_wheel_motor = joint;
1287 }
1288 }
1289 }
1290
GetMass() const1291 double RS_Limb::GetMass() const {
1292 double mass = 0;
1293 for (auto link : m_links)
1294 mass += link.second->m_body->GetMass();
1295 return mass;
1296 }
1297
SetVisualizationType(VisualizationType vis)1298 void RS_Limb::SetVisualizationType(VisualizationType vis) {
1299 for (auto link : m_links)
1300 link.second->SetVisualizationType(vis);
1301
1302 auto texture = chrono_types::make_shared<ChTexture>();
1303 texture->SetTextureFilename(GetChronoDataFile("textures/greenwhite.png"));
1304 m_wheel->m_body->AddAsset(texture);
1305 }
1306
Activate(const std::string & motor_name,double time,double val)1307 void RS_Limb::Activate(const std::string& motor_name, double time, double val) {
1308 auto itr = m_motors.find(motor_name);
1309 if (itr == m_motors.end()) {
1310 std::cout << "Limb::Activate -- Unknown motor " << motor_name << std::endl;
1311 return;
1312 }
1313
1314 // Note: currently hard-coded for angle motor
1315 auto fun = std::static_pointer_cast<ChFunction_Setpoint>(itr->second->GetMotorFunction());
1316 fun->SetSetpoint(-val, time);
1317 }
1318
GetMotorAngle(const std::string & motor_name) const1319 double RS_Limb::GetMotorAngle(const std::string& motor_name) const {
1320 auto itr = m_motors.find(motor_name);
1321 if (itr == m_motors.end()) {
1322 std::cout << "Limb::GetMotorAngle -- Unknown motor " << motor_name << std::endl;
1323 return 0;
1324 }
1325
1326 return itr->second->GetMotorRot();
1327 }
1328
GetMotorOmega(const std::string & motor_name) const1329 double RS_Limb::GetMotorOmega(const std::string& motor_name) const {
1330 auto itr = m_motors.find(motor_name);
1331 if (itr == m_motors.end()) {
1332 std::cout << "Limb::GetMotorOmega -- Unknown motor " << motor_name << std::endl;
1333 return 0;
1334 }
1335
1336 return itr->second->GetMotorRot_dt();
1337 }
1338
GetMotorTorque(const std::string & motor_name) const1339 double RS_Limb::GetMotorTorque(const std::string& motor_name) const {
1340 auto itr = m_motors.find(motor_name);
1341 if (itr == m_motors.end()) {
1342 std::cout << "Limb::GetMotorTorque -- Unknown motor " << motor_name << std::endl;
1343 return 0;
1344 }
1345
1346 return itr->second->GetMotorTorque();
1347 }
1348
1349 static std::string motor_names[] = {"joint1", "joint2", "joint3", "joint4", "joint5", "joint6", "joint7", "joint8"};
1350
Activate(double time,const std::array<double,8> & vals)1351 void RS_Limb::Activate(double time, const std::array<double, 8>& vals) {
1352 for (int i = 0; i < 8; i++) {
1353 auto fun = std::static_pointer_cast<ChFunction_Setpoint>(m_motors[motor_names[i]]->GetMotorFunction());
1354 fun->SetSetpoint(-vals[i], time);
1355 }
1356 }
1357
GetMotorAngles()1358 std::array<double, 8> RS_Limb::GetMotorAngles() {
1359 std::array<double, 8> result;
1360
1361 for (int i = 0; i < 8; i++) {
1362 result[i] = m_motors[motor_names[i]]->GetMotorRot();
1363 }
1364
1365 return result;
1366 }
1367
GetMotorOmegas()1368 std::array<double, 8> RS_Limb::GetMotorOmegas() {
1369 std::array<double, 8> result;
1370
1371 for (int i = 0; i < 8; i++) {
1372 result[i] = m_motors[motor_names[i]]->GetMotorRot_dt();
1373 }
1374
1375 return result;
1376 }
1377
GetMotorTorques()1378 std::array<double, 8> RS_Limb::GetMotorTorques() {
1379 std::array<double, 8> result;
1380
1381 for (int i = 0; i < 8; i++) {
1382 result[i] = m_motors[motor_names[i]]->GetMotorTorque();
1383 }
1384
1385 return result;
1386 }
1387
GetMotorActuations(std::array<double,8> & angles,std::array<double,8> & speeds)1388 void RS_Limb::GetMotorActuations(std::array<double, 8>& angles, std::array<double, 8>& speeds) {
1389 for (int i = 0; i < 8; i++) {
1390 auto fun = std::static_pointer_cast<ChFunction_Setpoint>(m_motors[motor_names[i]]->GetMotorFunction());
1391 // Note: the time passed as argument here does not matter for a Chfunction_Setpoint
1392 angles[i] = fun->Get_y(0);
1393 speeds[i] = fun->Get_y_dx(0);
1394 }
1395 }
1396
SetCollideLinks(bool state)1397 void RS_Limb::SetCollideLinks(bool state) {
1398 m_collide_links = state;
1399 for (auto link : m_links) {
1400 if (link.second != m_wheel)
1401 link.second->m_body->SetCollide(state);
1402 }
1403 }
1404
SetCollideWheel(bool state)1405 void RS_Limb::SetCollideWheel(bool state) {
1406 m_collide_wheel = state;
1407 m_wheel->m_body->SetCollide(state);
1408 }
1409
Translate(const ChVector<> & shift)1410 void RS_Limb::Translate(const ChVector<>& shift) {
1411 for (auto link : m_links) {
1412 link.second->m_body->SetPos(link.second->m_body->GetPos() + shift);
1413 }
1414 }
1415
1416 } // end namespace robosimian
1417 } // namespace chrono
1418