1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2014 projectchrono.org
5 // All rights 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 // Template for a rigid tire
16 //
17 // =============================================================================
18 
19 #include <algorithm>
20 
21 #include "chrono/core/ChGlobal.h"
22 #include "chrono/physics/ChSystem.h"
23 #include "chrono/physics/ChContactContainer.h"
24 
25 #include "chrono_vehicle/wheeled_vehicle/tire/ChRigidTire.h"
26 
27 #include "chrono_vehicle/terrain/SCMDeformableTerrain.h"
28 
29 namespace chrono {
30 namespace vehicle {
31 
32 // -----------------------------------------------------------------------------
33 // -----------------------------------------------------------------------------
ChRigidTire(const std::string & name)34 ChRigidTire::ChRigidTire(const std::string& name) : ChTire(name), m_use_contact_mesh(false), m_trimesh(nullptr) {}
35 
~ChRigidTire()36 ChRigidTire::~ChRigidTire() {}
37 
38 // -----------------------------------------------------------------------------
39 // -----------------------------------------------------------------------------
SetMeshFilename(const std::string & mesh_file,double sweep_sphere_radius)40 void ChRigidTire::SetMeshFilename(const std::string& mesh_file, double sweep_sphere_radius) {
41     m_use_contact_mesh = true;
42     m_contact_meshFile = mesh_file;
43     m_sweep_sphere_radius = sweep_sphere_radius;
44 }
45 
46 // -----------------------------------------------------------------------------
47 // -----------------------------------------------------------------------------
Initialize(std::shared_ptr<ChWheel> wheel)48 void ChRigidTire::Initialize(std::shared_ptr<ChWheel> wheel) {
49     ChTire::Initialize(wheel);
50 
51     auto wheel_body = wheel->GetSpindle();
52 
53     CreateContactMaterial(wheel_body->GetSystem()->GetContactMethod());
54     assert(m_material && m_material->GetContactMethod() == wheel_body->GetSystem()->GetContactMethod());
55 
56     wheel_body->SetCollide(true);
57 
58     wheel_body->GetCollisionModel()->ClearModel();
59 
60     wheel_body->GetCollisionModel()->SetFamily(WheeledCollisionFamily::TIRES);
61 
62     if (m_use_contact_mesh) {
63         // Mesh contact
64         m_trimesh = chrono_types::make_shared<geometry::ChTriangleMeshConnected>();
65         m_trimesh->LoadWavefrontMesh(m_contact_meshFile, true, false);
66 
67         //// RADU
68         // Hack to deal with current limitation: cannot set offset on a trimesh collision shape!
69         double offset = GetOffset();
70         if (std::abs(offset) > 1e-3) {
71             for (int i = 0; i < m_trimesh->m_vertices.size(); i++)
72                 m_trimesh->m_vertices[i].y() += offset;
73         }
74 
75         wheel_body->GetCollisionModel()->AddTriangleMesh(m_material, m_trimesh, false, false, ChVector<>(0),
76                                                          ChMatrix33<>(1), m_sweep_sphere_radius);
77     } else {
78         // Cylinder contact
79         wheel_body->GetCollisionModel()->AddCylinder(m_material, GetRadius(), GetRadius(), GetWidth() / 2,
80                                                      ChVector<>(0, GetOffset(), 0));
81     }
82 
83     wheel_body->GetCollisionModel()->BuildModel();
84 }
85 
86 // -----------------------------------------------------------------------------
87 // -----------------------------------------------------------------------------
AddVisualizationAssets(VisualizationType vis)88 void ChRigidTire::AddVisualizationAssets(VisualizationType vis) {
89     if (vis == VisualizationType::NONE)
90         return;
91 
92     m_cyl_shape = chrono_types::make_shared<ChCylinderShape>();
93     m_cyl_shape->GetCylinderGeometry().rad = GetRadius();
94     m_cyl_shape->GetCylinderGeometry().p1 = ChVector<>(0, GetOffset() + GetWidth() / 2, 0);
95     m_cyl_shape->GetCylinderGeometry().p2 = ChVector<>(0, GetOffset() - GetWidth() / 2, 0);
96     m_wheel->GetSpindle()->AddAsset(m_cyl_shape);
97 
98     m_texture = chrono_types::make_shared<ChTexture>();
99     m_texture->SetTextureFilename(GetChronoDataFile("textures/greenwhite.png"));
100     m_wheel->GetSpindle()->AddAsset(m_texture);
101 }
102 
RemoveVisualizationAssets()103 void ChRigidTire::RemoveVisualizationAssets() {
104     // Make sure we only remove the assets added by ChRigidTire::AddVisualizationAssets.
105     // This is important for the ChTire object because a wheel may add its own assets
106     // to the same body (the spindle/wheel).
107     auto& assets = m_wheel->GetSpindle()->GetAssets();
108     {
109         auto it = std::find(assets.begin(), assets.end(), m_cyl_shape);
110         if (it != assets.end())
111             assets.erase(it);
112     }
113     {
114         auto it = std::find(assets.begin(), assets.end(), m_texture);
115         if (it != assets.end())
116             assets.erase(it);
117     }
118 }
119 
120 // -----------------------------------------------------------------------------
121 // -----------------------------------------------------------------------------
122 
123 // Callback class to process contacts on a rigid tire.
124 // Accumulate contact forces and torques on the associated wheel body.
125 // Express them in the global frame, as applied to the wheel center.
126 class RigidTireContactReporter : public ChContactContainer::ReportContactCallback {
127   public:
RigidTireContactReporter(std::shared_ptr<ChBody> body)128     RigidTireContactReporter(std::shared_ptr<ChBody> body) : m_body(body) {}
129 
130     // Accumulated force, expressed in global frame, applied to wheel center.
GetAccumulatedForce() const131     const ChVector<>& GetAccumulatedForce() const { return m_force; }
132 
133     // Accumulated torque, expressed in global frame.
GetAccumulatedTorque() const134     const ChVector<>& GetAccumulatedTorque() const { return m_torque; }
135 
136   private:
OnReportContact(const ChVector<> & pA,const ChVector<> & pB,const ChMatrix33<> & plane_coord,const double & distance,const double & eff_radius,const ChVector<> & rforce,const ChVector<> & rtorque,ChContactable * modA,ChContactable * modB)137     virtual bool OnReportContact(const ChVector<>& pA,
138                                  const ChVector<>& pB,
139                                  const ChMatrix33<>& plane_coord,
140                                  const double& distance,
141                                  const double& eff_radius,
142                                  const ChVector<>& rforce,
143                                  const ChVector<>& rtorque,
144                                  ChContactable* modA,
145                                  ChContactable* modB) override {
146         // Filter contacts that involve the tire body.
147         if (modA == m_body.get() || modB == m_body.get()) {
148             // Express current contact force and torque in global frame
149             ChVector<> force = plane_coord * rforce;
150             ChVector<> torque = plane_coord * rtorque;
151             // Wheel center in global frame
152             const ChVector<>& center = m_body->GetPos();
153             // Accumulate
154             m_force += force;
155             m_torque += torque + Vcross(Vsub(pA, center), force);
156         }
157 
158         return true;
159     }
160 
161     std::shared_ptr<ChBody> m_body;
162     ChVector<> m_force;
163     ChVector<> m_torque;
164 };
165 
GetTireForce() const166 TerrainForce ChRigidTire::GetTireForce() const {
167     // A ChRigidTire always returns zero force and moment since tire forces are automatically applied
168     // to the associated wheel through Chrono's frictional contact system.
169     TerrainForce tire_force;
170     tire_force.point = m_wheel->GetPos();
171     tire_force.force = ChVector<>(0, 0, 0);
172     tire_force.moment = ChVector<>(0, 0, 0);
173 
174     return tire_force;
175 }
176 
ReportTireForce(ChTerrain * terrain) const177 TerrainForce ChRigidTire::ReportTireForce(ChTerrain* terrain) const {
178     // If interacting with an SCM terrain, interrogate the terrain system
179     // for the cumulative force on the associated rigid body.
180     if (auto scm = dynamic_cast<SCMDeformableTerrain*>(terrain)) {
181         return scm->GetContactForce(m_wheel->GetSpindle());
182     }
183 
184     // Otherwise, calculate and return the resultant of the contact forces acting on the tire.
185     // The resulting tire force and moment are expressed in global frame, as applied at the center
186     // of the associated spindle body.
187 
188     TerrainForce tire_force;
189     tire_force.point = m_wheel->GetSpindle()->GetPos();
190     tire_force.force = m_wheel->GetSpindle()->GetContactForce();
191     tire_force.moment = m_wheel->GetSpindle()->GetContactTorque();
192 
193     // Approach using the RigidTireContactReporter does not work in Chrono::Multicore
194     // since contact forces and torques passed to OnReportContact are always zero.
195     /*
196     auto reporter = chrono_types::make_shared<RigidTireContactReporter>(m_wheel);
197     m_wheel->GetSpindle()->GetSystem()->GetContactContainer()->ReportAllContacts(&reporter);
198     TerrainForce tire_force;
199     tire_force.point = m_wheel->GetSpindle()->GetPos();
200     tire_force.force = reporter->GetAccumulatedForce();
201     tire_force.moment = reporter->GetAccumulatedTorque();
202     */
203 
204     return tire_force;
205 }
206 
207 // -----------------------------------------------------------------------------
208 // -----------------------------------------------------------------------------
GetNumVertices() const209 unsigned int ChRigidTire::GetNumVertices() const {
210     assert(m_use_contact_mesh);
211     return static_cast<unsigned int>(m_trimesh->getCoordsVertices().size());
212 }
213 
GetNumNormals() const214 unsigned int ChRigidTire::GetNumNormals() const {
215     assert(m_use_contact_mesh);
216     return static_cast<unsigned int>(m_trimesh->getCoordsNormals().size());
217 }
218 
GetNumTriangles() const219 unsigned int ChRigidTire::GetNumTriangles() const {
220     assert(m_use_contact_mesh);
221     return static_cast<unsigned int>(m_trimesh->getIndicesVertexes().size());
222 }
223 
GetMeshConnectivity() const224 const std::vector<ChVector<int>>& ChRigidTire::GetMeshConnectivity() const {
225     assert(m_use_contact_mesh);
226     return m_trimesh->getIndicesVertexes();
227 }
228 
GetMeshNormalIndices() const229 const std::vector<ChVector<int>>& ChRigidTire::GetMeshNormalIndices() const {
230     assert(m_use_contact_mesh);
231     return m_trimesh->getIndicesNormals();
232 }
233 
GetMeshVertices() const234 const std::vector<ChVector<>>& ChRigidTire::GetMeshVertices() const {
235     assert(m_use_contact_mesh);
236     return m_trimesh->getCoordsVertices();
237 }
238 
GetMeshNormals() const239 const std::vector<ChVector<>>& ChRigidTire::GetMeshNormals() const {
240     assert(m_use_contact_mesh);
241     return m_trimesh->getCoordsNormals();
242 }
243 
GetMeshVertexStates(std::vector<ChVector<>> & pos,std::vector<ChVector<>> & vel) const244 void ChRigidTire::GetMeshVertexStates(std::vector<ChVector<>>& pos, std::vector<ChVector<>>& vel) const {
245     assert(m_use_contact_mesh);
246     auto vertices = m_trimesh->getCoordsVertices();
247 
248     for (size_t i = 0; i < vertices.size(); ++i) {
249         pos.push_back(m_wheel->GetSpindle()->TransformPointLocalToParent(vertices[i]));
250         vel.push_back(m_wheel->GetSpindle()->PointSpeedLocalToParent(vertices[i]));
251     }
252 }
253 
254 }  // end namespace vehicle
255 }  // end namespace chrono
256