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 // Demo code illustrating the SCM semi-empirical model for deformable soil
13 // =============================================================================
14 
15 #include "chrono/geometry/ChTriangleMeshConnected.h"
16 #include "chrono/physics/ChLinkMotorRotationAngle.h"
17 #include "chrono/physics/ChLoadContainer.h"
18 #include "chrono/physics/ChSystemSMC.h"
19 #include "chrono/utils/ChUtilsInputOutput.h"
20 #ifdef CHRONO_COLLISION
21     #include "chrono/collision/ChCollisionSystemChrono.h"
22 #endif
23 
24 #include "chrono_irrlicht/ChIrrApp.h"
25 
26 #include "chrono_vehicle/ChVehicleModelData.h"
27 #include "chrono_vehicle/terrain/SCMDeformableTerrain.h"
28 
29 #include "chrono_thirdparty/filesystem/path.h"
30 
31 using namespace chrono;
32 using namespace chrono::irrlicht;
33 
34 using namespace irr;
35 
36 bool output = false;
37 const std::string out_dir = GetChronoOutputPath() + "SCM_DEF_SOIL";
38 
39 // Type of tire (controls both contact and visualization)
40 enum class TireType { CYLINDRICAL, LUGGED };
41 TireType tire_type = TireType::LUGGED;
42 
43 // SCM grid spacing
44 double mesh_resolution = 0.04;
45 
46 // Enable/disable bulldozing effects
47 bool enable_bulldozing = true;
48 
49 // Enable/disable moving patch feature
50 bool enable_moving_patch = true;
51 
52 // If true, use provided callback to change soil properties based on location
53 bool var_params = true;
54 
55 // Custom callback for setting location-dependent soil properties.
56 // Note that the location is given in the SCM reference frame.
57 // Here, the vehicle moves in the terrain's negative y direction!
58 class MySoilParams : public vehicle::SCMDeformableTerrain::SoilParametersCallback {
59   public:
Set(const ChVector<> & loc,double & Bekker_Kphi,double & Bekker_Kc,double & Bekker_n,double & Mohr_cohesion,double & Mohr_friction,double & Janosi_shear,double & elastic_K,double & damping_R)60     virtual void Set(const ChVector<>& loc,
61                      double& Bekker_Kphi,
62                      double& Bekker_Kc,
63                      double& Bekker_n,
64                      double& Mohr_cohesion,
65                      double& Mohr_friction,
66                      double& Janosi_shear,
67                      double& elastic_K,
68                      double& damping_R) override {
69         if (loc.y() > 0) {
70             Bekker_Kphi = 0.2e6;
71             Bekker_Kc = 0;
72             Bekker_n = 1.1;
73             Mohr_cohesion = 0;
74             Mohr_friction = 30;
75             Janosi_shear = 0.01;
76             elastic_K = 4e7;
77             damping_R = 3e4;
78         } else {
79             Bekker_Kphi = 5301e3;
80             Bekker_Kc = 102e3;
81             Bekker_n = 0.793;
82             Mohr_cohesion = 1.3e3;
83             Mohr_friction = 31.1;
84             Janosi_shear = 1.2e-2;
85             elastic_K = 4e8;
86             damping_R = 3e4;
87         }
88     }
89 };
90 
main(int argc,char * argv[])91 int main(int argc, char* argv[]) {
92     GetLog() << "Copyright (c) 2017 projectchrono.org\nChrono version: " << CHRONO_VERSION << "\n\n";
93 
94     // Set world frame with Y up
95     vehicle::ChWorldFrame::SetYUP();
96 
97     // Global parameter for tire:
98     double tire_rad = 0.8;
99     ChVector<> tire_center(0, 0.02 + tire_rad, -1.5);
100 
101     // Create a Chrono::Engine physical system
102     auto collsys_type = collision::ChCollisionSystemType::BULLET;
103     ChSystemSMC my_system;
104     my_system.SetNumThreads(4, 8, 1);
105     if (collsys_type == collision::ChCollisionSystemType::CHRONO) {
106 #ifdef CHRONO_COLLISION
107         auto collsys = chrono_types::make_shared<collision::ChCollisionSystemChrono>();
108         collsys->SetBroadphaseGridResolution(ChVector<int>(20, 20, 10));
109         my_system.SetCollisionSystem(collsys);
110 #endif
111     }
112 
113     // Create the Irrlicht visualization (open the Irrlicht device,
114     // bind a simple user interface, etc. etc.)
115     ChIrrApp application(&my_system, L"Deformable soil", core::dimension2d<u32>(1280, 720), VerticalDir::Y, false, true);
116 
117     // Easy shortcuts to add camera, lights, logo and sky in Irrlicht scene:
118     application.AddTypicalLogo();
119     application.AddTypicalSky();
120     application.AddTypicalLights();
121     application.AddTypicalCamera(core::vector3df(2.0f, 1.4f, 0.0f), core::vector3df(0, (f32)tire_rad, 0));
122     application.AddLightWithShadow(core::vector3df(1.5f, 5.5f, -2.5f), core::vector3df(0, 0, 0), 3, 2.2, 7.2, 40, 512,
123                                    video::SColorf(0.8f, 0.8f, 1.0f));
124 
125     auto mtruss = chrono_types::make_shared<ChBody>(collsys_type);
126     mtruss->SetBodyFixed(true);
127     my_system.Add(mtruss);
128 
129     // Initialize output
130     if (output) {
131         if (!filesystem::create_directory(filesystem::path(out_dir))) {
132             std::cout << "Error creating directory " << out_dir << std::endl;
133             return 1;
134         }
135     }
136     utils::CSV_writer csv(" ");
137 
138     //
139     // Create a rigid body with a mesh or a cylinder collision shape
140     //
141 
142     auto mrigidbody = chrono_types::make_shared<ChBody>(collsys_type);
143     my_system.Add(mrigidbody);
144     mrigidbody->SetMass(500);
145     mrigidbody->SetInertiaXX(ChVector<>(20, 20, 20));
146     mrigidbody->SetPos(tire_center + ChVector<>(0, 0.3, 0));
147 
148     auto material = chrono_types::make_shared<ChMaterialSurfaceSMC>();
149     mrigidbody->GetCollisionModel()->ClearModel();
150     switch (tire_type) {
151         case TireType::LUGGED: {
152             auto trimesh = chrono_types::make_shared<geometry::ChTriangleMeshConnected>();
153             trimesh->LoadWavefrontMesh(GetChronoDataFile("models/tractor_wheel/tractor_wheel.obj"));
154 
155             std::shared_ptr<ChTriangleMeshShape> mrigidmesh(new ChTriangleMeshShape);
156             mrigidmesh->SetMesh(trimesh);
157             mrigidbody->AddAsset(mrigidmesh);
158 
159             mrigidbody->GetCollisionModel()->AddTriangleMesh(material, trimesh, false, false, VNULL, ChMatrix33<>(1),
160                                                              0.01);
161             break;
162         }
163         case TireType::CYLINDRICAL: {
164             double radius = 0.5;
165             double width = 0.4;
166             mrigidbody->GetCollisionModel()->AddCylinder(material, radius, radius, width / 2, ChVector<>(0), Q_from_AngZ(CH_C_PI_2));
167 
168             auto cyl_shape = chrono_types::make_shared<ChCylinderShape>();
169             cyl_shape->GetCylinderGeometry().rad = radius;
170             cyl_shape->GetCylinderGeometry().p1 = ChVector<>(+width / 2, 0, 0);
171             cyl_shape->GetCylinderGeometry().p2 = ChVector<>(-width / 2, 0, 0);
172             mrigidbody->AddAsset(cyl_shape);
173 
174             break;
175         }
176     }
177     mrigidbody->GetCollisionModel()->BuildModel();
178     mrigidbody->SetCollide(true);
179 
180     std::shared_ptr<ChColorAsset> mcol(new ChColorAsset);
181     mcol->SetColor(ChColor(0.3f, 0.3f, 0.3f));
182     mrigidbody->AddAsset(mcol);
183 
184     auto motor = chrono_types::make_shared<ChLinkMotorRotationAngle>();
185     motor->SetSpindleConstraint(ChLinkMotorRotation::SpindleConstraint::OLDHAM);
186     motor->SetAngleFunction(chrono_types::make_shared<ChFunction_Ramp>(0, CH_C_PI / 4.0));
187     motor->Initialize(mrigidbody, mtruss, ChFrame<>(tire_center, Q_from_AngY(CH_C_PI_2)));
188     my_system.Add(motor);
189 
190     //
191     // THE DEFORMABLE TERRAIN
192     //
193 
194     // Create the 'deformable terrain' object
195     vehicle::SCMDeformableTerrain mterrain(&my_system);
196 
197     // Displace/rotate the terrain reference plane.
198     // Note that SCMDeformableTerrain uses a default ISO reference frame (Z up). Since the mechanism is modeled here in
199     // a Y-up global frame, we rotate the terrain plane by -90 degrees about the X axis.
200     mterrain.SetPlane(ChCoordsys<>(ChVector<>(0, 0.2, 0), Q_from_AngX(-CH_C_PI_2)));
201 
202     // Initialize the geometry of the soil
203 
204     // Use either a regular grid:
205     double length = 6;
206     double width = 2;
207     mterrain.Initialize(width, length, mesh_resolution);
208 
209     // Or use a height map:
210     ////mterrain.Initialize(vehicle::GetDataFile("terrain/height_maps/test64.bmp"), "test64", 1.6, 1.6, 0, 0.3);
211 
212     // Set the soil terramechanical parameters
213     if (var_params) {
214         // Location-dependent soil properties
215         auto my_params = chrono_types::make_shared<MySoilParams>();
216         mterrain.RegisterSoilParametersCallback(my_params);
217     } else {
218         // Constant soil properties
219         mterrain.SetSoilParameters(0.2e6,  // Bekker Kphi
220                                    0,      // Bekker Kc
221                                    1.1,    // Bekker n exponent
222                                    0,      // Mohr cohesive limit (Pa)
223                                    30,     // Mohr friction limit (degrees)
224                                    0.01,   // Janosi shear coefficient (m)
225                                    4e7,    // Elastic stiffness (Pa/m), before plastic yield, must be > Kphi
226                                    3e4     // Damping (Pa s/m), proportional to negative vertical speed (optional)
227         );
228 
229         // LETE sand parameters
230         ////mterrain.SetSoilParameters(5301e3,  // Bekker Kphi
231         ////                           102e3,   // Bekker Kc
232         ////                           0.793,   // Bekker n exponent
233         ////                           1.3e3,   // Mohr cohesive limit (Pa)
234         ////                           31.1,    // Mohr friction limit (degrees)
235         ////                           1.2e-2,  // Janosi shear coefficient (m)
236         ////                           4e8,     // Elastic stiffness (Pa/m), before plastic yield, must be > Kphi
237         ////                           3e4      // Damping (Pa s/m), proportional to negative vertical speed (optional)
238         ////);
239     }
240 
241     if (enable_bulldozing) {
242         mterrain.EnableBulldozing(true);  // inflate soil at the border of the rut
243         mterrain.SetBulldozingParameters(
244             55,   // angle of friction for erosion of displaced material at the border of the rut
245             1,    // displaced material vs downward pressed material.
246             5,    // number of erosion refinements per timestep
247             6);  // number of concentric vertex selections subject to erosion
248     }
249 
250     // Optionally, enable moving patch feature (reduces number of ray casts)
251     if (enable_moving_patch) {
252         mterrain.AddMovingPatch(mrigidbody, ChVector<>(0, 0, 0), ChVector<>(0.5, 2 * tire_rad, 2 * tire_rad));
253     }
254 
255     // Set some visualization parameters: either with a texture, or with falsecolor plot, etc.
256     ////mterrain.SetTexture(vehicle::GetDataFile("terrain/textures/grass.jpg"), 16, 16);
257     mterrain.SetPlotType(vehicle::SCMDeformableTerrain::PLOT_PRESSURE, 0, 30000.2);
258     ////mterrain.SetPlotType(vehicle::SCMDeformableTerrain::PLOT_PRESSURE_YELD, 0, 30000.2);
259     ////mterrain.SetPlotType(vehicle::SCMDeformableTerrain::PLOT_SINKAGE, 0, 0.15);
260     ////mterrain.SetPlotType(vehicle::SCMDeformableTerrain::PLOT_SINKAGE_PLASTIC, 0, 0.15);
261     ////mterrain.SetPlotType(vehicle::SCMDeformableTerrain::PLOT_SINKAGE_ELASTIC, 0, 0.05);
262     ////mterrain.SetPlotType(vehicle::SCMDeformableTerrain::PLOT_STEP_PLASTIC_FLOW, 0, 0.0001);
263     ////mterrain.SetPlotType(vehicle::SCMDeformableTerrain::PLOT_ISLAND_ID, 0, 8);
264     ////mterrain.SetPlotType(vehicle::SCMDeformableTerrain::PLOT_IS_TOUCHED, 0, 8);
265     mterrain.SetMeshWireframe(true);
266 
267     // ==IMPORTANT!== Use this function for adding a ChIrrNodeAsset to all items
268     application.AssetBindAll();
269 
270     // ==IMPORTANT!== Use this function for 'converting' into Irrlicht meshes the assets
271     application.AssetUpdateAll();
272 
273     // Use shadows in realtime view
274     application.AddShadowAll();
275 
276     //
277     // THE SOFT-REAL-TIME CYCLE
278     //
279     /*
280         // Change the timestepper to HHT:
281         my_system.SetTimestepperType(ChTimestepper::Type::HHT);
282         auto integrator = std::static_pointer_cast<ChTimestepperHHT>(my_system.GetTimestepper());
283         integrator->SetAlpha(-0.2);
284         integrator->SetMaxiters(8);
285         integrator->SetAbsTolerances(1e-05, 1.8e00);
286         integrator->SetMode(ChTimestepperHHT::POSITION);
287         integrator->SetModifiedNewton(true);
288         integrator->SetScaling(true);
289         integrator->SetVerbose(true);
290     */
291     /*
292         my_system.SetTimestepperType(ChTimestepper::Type::EULER_IMPLICIT);
293     */
294 
295     application.SetTimestep(0.002);
296 
297     while (application.GetDevice()->run()) {
298         double time = my_system.GetChTime();
299         if (output) {
300             vehicle::TerrainForce frc = mterrain.GetContactForce(mrigidbody);
301             csv << time << frc.force << frc.moment << frc.point << std::endl;
302         }
303 
304         ////std::cout << "\nTime: " << time << std::endl;
305         ////std::cout << "Wheel pos: " << mrigidbody->GetPos() << std::endl;
306         ////std::cout << "Wheel rot: " << mrigidbody->GetRot() << std::endl;
307 
308         application.BeginScene();
309         application.GetActiveCamera()->setTarget(core::vector3dfCH(mrigidbody->GetPos()));
310         application.DrawAll();
311         application.DoStep();
312         tools::drawColorbar(0, 30000, "Pressure yield [Pa]", application.GetDevice(), 1180);
313         application.EndScene();
314 
315         ////mterrain.PrintStepStatistics(std::cout);
316     }
317 
318     if (output) {
319         csv.write_to_file(out_dir + "/output.dat");
320     }
321 
322     return 0;
323 }
324