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