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