1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2020 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: Jay Taves
13 // =============================================================================
14 //
15 // Demo code illustrating synchronization of the SCM semi-empirical model for
16 // deformable soil
17 //
18 // See also in chrono_vehicle:
19 // - demo_VEH_DeformableSoil
20 // - demo_VEH_DeformableSoilAndTire
21 // - demo_VEH_M113_DefSoil
22 //
23 // =============================================================================
24 
25 #include <cmath>
26 
27 #include "chrono/core/ChRealtimeStep.h"
28 
29 #include "chrono_vehicle/ChConfigVehicle.h"
30 #include "chrono_vehicle/ChVehicleModelData.h"
31 #include "chrono_vehicle/terrain/SCMDeformableTerrain.h"
32 #include "chrono_vehicle/driver/ChPathFollowerDriver.h"
33 
34 #include "chrono_models/vehicle/m113/M113.h"
35 
36 #include "chrono_synchrono/SynConfig.h"
37 #include "chrono_synchrono/SynChronoManager.h"
38 #include "chrono_synchrono/agent/SynTrackedVehicleAgent.h"
39 #include "chrono_synchrono/agent/SynSCMTerrainAgent.h"
40 #include "chrono_synchrono/communication/mpi/SynMPICommunicator.h"
41 #include "chrono_synchrono/utils/SynDataLoader.h"
42 #include "chrono_synchrono/utils/SynLog.h"
43 
44 #ifdef CHRONO_IRRLICHT
45     #include "chrono_vehicle/tracked_vehicle/utils/ChTrackedVehicleIrrApp.h"
46 #endif
47 
48 #ifdef CHRONO_SENSOR
49     #include "chrono_sensor/ChSensorManager.h"
50     #include "chrono_sensor/sensors/ChCameraSensor.h"
51     #include "chrono_sensor/filters/ChFilterAccess.h"
52     #include "chrono_sensor/filters/ChFilterSave.h"
53     #include "chrono_sensor/filters/ChFilterVisualize.h"
54 using namespace chrono::sensor;
55 #endif
56 
57 #include "chrono_thirdparty/cxxopts/ChCLI.h"
58 
59 using namespace chrono;
60 using namespace chrono::vehicle;
61 using namespace chrono::synchrono;
62 using namespace chrono::vehicle::m113;
63 
64 // Forward declaration
65 void AddCommandLineOptions(ChCLI& cli);
66 
67 // =============================================================================
68 
main(int argc,char * argv[])69 int main(int argc, char* argv[]) {
70     // -----------------------
71     // Create SynChronoManager
72     // -----------------------
73     auto communicator = chrono_types::make_shared<SynMPICommunicator>(argc, argv);
74     int node_id = communicator->GetRank();
75     int num_nodes = communicator->GetNumRanks();
76     SynChronoManager syn_manager(node_id, num_nodes, communicator);
77 
78     // Copyright
79     if (node_id == 0) {
80         SynLog() << "Copyright (c) 2020 projectchrono.org\n";
81         SynLog() << "Chrono version: " << CHRONO_VERSION << "\n\n";
82     }
83 
84     // -----------------------------------------------------
85     // CLI SETUP - Get most parameters from the command line
86     // -----------------------------------------------------
87 
88     ChCLI cli(argv[0]);
89     AddCommandLineOptions(cli);
90     if (!cli.Parse(argc, argv, node_id == 0))
91         return 0;
92 
93     // Normal simulation options
94     auto step_size = cli.GetAsType<double>("step_size");
95     auto end_time = cli.GetAsType<double>("end_time");
96     auto heartbeat = cli.GetAsType<double>("heartbeat");
97     auto contact_method =
98         (cli.Matches<std::string>("contact_method", "SMC") ? ChContactMethod::SMC : ChContactMethod::NSC);
99     auto size_x = cli.GetAsType<double>("sizeX");
100     auto size_y = cli.GetAsType<double>("sizeY");
101     auto dpu = cli.GetAsType<double>("dpu");
102 #ifdef CHRONO_SENSOR
103     auto cam_x = cli.GetAsType<std::vector<double>>("c_pos")[0];
104     auto cam_y = cli.GetAsType<std::vector<double>>("c_pos")[1];
105     auto cam_res_width = cli.GetAsType<std::vector<int>>("res")[0];
106     auto cam_res_height = cli.GetAsType<std::vector<int>>("res")[1];
107 #endif
108     auto flat_patch = cli.Matches<std::string>("terrain_type", "Flat");
109     auto bulldozing = cli.GetAsType<bool>("bulldozing");
110 
111     // Change SynChronoManager settings
112     syn_manager.SetHeartbeat(heartbeat);
113 
114     // ----------------------
115     // Vehicle Initialization
116     // ----------------------
117     // Calculate initial position and paths for each vehicle
118     // Use up more of the mesh by not placing vehicles in the middle
119     ChVector<> offset(-size_x / 2 + 5, 0, 0);
120 
121     ChVector<> initLoc;
122     ChQuaternion<> initRot;
123     std::vector<ChVector<>> curve_pts;
124     if (node_id % 2 == 0) {
125         // Start even vehicles in a row on the south side, driving north
126         initLoc = offset + ChVector<>(0, 2.0 * (node_id + 1), 1.1);
127         initRot = Q_from_AngZ(0);
128         curve_pts = {initLoc, initLoc + ChVector<>(100, 0, 0)};
129     } else {
130         // Start odd vehicles staggered going up the west edge, driving east
131         initLoc = offset + ChVector<>(2.0 * (node_id - 1), -5.0 - 2.0 * (node_id - 1), 1.1);
132         initRot = Q_from_AngZ(CH_C_PI / 2);
133         curve_pts = {initLoc, initLoc + ChVector<>(0, 100, 0)};
134     }
135 
136     // Create the M113
137     M113 m113;
138     m113.SetContactMethod(contact_method);
139     m113.SetChassisCollisionType(CollisionType::NONE);
140     m113.SetChassisFixed(false);
141     m113.SetInitPosition(ChCoordsys<>(initLoc, initRot));
142     m113.SetTrackShoeType(TrackShoeType::SINGLE_PIN);
143     m113.SetBrakeType(BrakeType::SIMPLE);
144     m113.SetDrivelineType(DrivelineTypeTV::BDS);
145     m113.SetPowertrainType(PowertrainModelType::SHAFTS);
146     m113.Initialize();
147 
148     m113.SetChassisVisualizationType(VisualizationType::MESH);
149     m113.SetSprocketVisualizationType(VisualizationType::MESH);
150     m113.SetIdlerVisualizationType(VisualizationType::MESH);
151     m113.SetRoadWheelAssemblyVisualizationType(VisualizationType::NONE);
152     m113.SetRoadWheelVisualizationType(VisualizationType::MESH);
153     m113.SetTrackShoeVisualizationType(VisualizationType::MESH);
154 
155     // Solver settings.
156     m113.GetSystem()->SetNumThreads(std::min(8, ChOMP::GetNumProcs()));
157     m113.GetSystem()->SetSolverMaxIterations(50);
158 
159     // Add vehicle as an agent
160     auto vehicle_agent = chrono_types::make_shared<SynTrackedVehicleAgent>(&m113.GetVehicle(),
161                                                                            synchrono::GetDataFile("vehicle/M113.json"));
162     syn_manager.AddAgent(vehicle_agent);
163 
164     // ----------------------
165     // Terrain specific setup
166     // ----------------------
167     auto terrain = chrono_types::make_shared<SCMDeformableTerrain>(m113.GetSystem());
168 
169     // Configure the SCM terrain
170     if (bulldozing) {
171         terrain->EnableBulldozing(bulldozing);
172         terrain->SetBulldozingParameters(
173             55,   // angle of friction for erosion of displaced material at the border of the rut
174             1,    // displaced material vs downward pressed material.
175             5,    // number of erosion refinements per timestep
176             10);  // number of concentric vertex selections subject to erosion
177     }
178 
179     // Only relevant for Irrlicht visualization, gives some nice colors
180     terrain->SetPlotType(SCMDeformableTerrain::PLOT_SINKAGE, 0, 0.1);
181     terrain->GetMesh()->SetWireframe(true);
182 
183     // The physics do not change when you add a moving patch, you just make it much easier for the SCM
184     // implementation to do its job by restricting where it has to look for contacts
185     terrain->AddMovingPatch(m113.GetVehicle().GetChassisBody(), ChVector<>(0, 0, 0), ChVector<>(10, 10, 1));
186 
187     if (flat_patch) {
188         terrain->Initialize(size_x, size_y, 1 / dpu);
189     } else {
190         terrain->Initialize(vehicle::GetDataFile("terrain/height_maps/slope.bmp"), size_x, size_y, 0.0, 5.0, 1 / dpu);
191     }
192 
193     // Create an SCMTerrainAgent and add it to the SynChrono manager
194     auto terrain_agent = chrono_types::make_shared<SynSCMTerrainAgent>(terrain);
195     syn_manager.AddAgent(terrain_agent);
196 
197     // Choice of soft parameters is arbitrary
198     SCMParameters params;
199     params.InitializeParametersAsMid();
200     terrain_agent->SetSoilParametersFromStruct(&params);
201 
202     // Add texture for the terrain
203     auto vis_mat = chrono_types::make_shared<ChVisualMaterial>();
204     vis_mat->SetSpecularColor({.1f, .1f, .1f});
205     vis_mat->SetKdTexture(GetChronoDataFile("sensor/textures/grass_texture.jpg"));
206     terrain->GetMesh()->material_list.push_back(vis_mat);
207 
208     // Create the driver for the vehicle
209 
210     // What we defined earlier, a straight line
211     auto path = chrono_types::make_shared<ChBezierCurve>(curve_pts);
212     ChPathFollowerDriver driver(m113.GetVehicle(), path, "Box path", 10);
213 
214     // Reasonable defaults for the underlying PID
215     driver.GetSpeedController().SetGains(0.4, 0, 0);
216     driver.GetSteeringController().SetGains(0.4, 0.1, 0.2);
217     driver.GetSteeringController().SetLookAheadDistance(5);
218 
219     // Initialzie the SynChrono manager
220     syn_manager.Initialize(m113.GetSystem());
221 
222     // -------------
223     // Visualization
224     // -------------
225 #ifdef CHRONO_IRRLICHT
226     // Create the vehicle Irrlicht interface
227     std::shared_ptr<ChTrackedVehicleIrrApp> app;
228     if (cli.HasValueInVector<int>("irr", node_id)) {
229         app = chrono_types::make_shared<ChTrackedVehicleIrrApp>(&m113.GetVehicle(), L"SynChrono SCM Demo");
230         app->SetSkyBox();
231         app->AddTypicalLights(irr::core::vector3df(30.f, -30.f, 100.f), irr::core::vector3df(30.f, 50.f, 100.f), 250,
232                               130);
233         app->SetChaseCamera(ChVector<>(0.0, 0.0, 1.75), 10.0, 0.5);
234         app->SetTimestep(step_size);
235         app->AssetBindAll();
236         app->AssetUpdateAll();
237     }
238 
239     // Time interval between two render frames (1/FPS)
240     double render_step_size = 1.0 / 100;
241     // Number of simulation steps between two render frames
242     int render_steps = (int)std::ceil(render_step_size / step_size);
243 #endif
244 
245 #ifdef CHRONO_SENSOR
246     ChSensorManager sensor_manager(m113.GetSystem());
247     if (cli.HasValueInVector<int>("sens", node_id)) {
248         // Give the camera a fixed place to live
249         auto origin = chrono_types::make_shared<ChBody>();
250         origin->SetBodyFixed(true);
251         m113.GetSystem()->AddBody(origin);
252 
253         // Happens to be a reasonable-looking height
254         ChVector<> camera_loc(cam_x, cam_y, 25);
255 
256         // Rotations to get a nice angle
257         ChQuaternion<> rotation = QUNIT;
258         const bool USE_ISO_VIEW = true;
259         if (USE_ISO_VIEW) {
260             ChQuaternion<> qA = Q_from_AngAxis(35 * CH_C_DEG_TO_RAD, VECT_Y);
261             ChQuaternion<> qB = Q_from_AngAxis(135 * CH_C_DEG_TO_RAD, VECT_Z);
262             rotation = rotation >> qA >> qB;
263         } else {
264             // Top down view
265             ChQuaternion<> qA = Q_from_AngAxis(90 * CH_C_DEG_TO_RAD, VECT_Y);
266             ChQuaternion<> qB = Q_from_AngAxis(180 * CH_C_DEG_TO_RAD, VECT_Z);
267             rotation = rotation >> qA >> qB;
268         }
269 
270         auto overhead_camera = chrono_types::make_shared<chrono::sensor::ChCameraSensor>(
271             origin,                                         // body camera is attached to
272             30.0f,                                          // update rate in Hz
273             chrono::ChFrame<double>(camera_loc, rotation),  // offset pose
274             cam_res_width,                                  // image width
275             cam_res_height,                                 // image height
276             (float)CH_C_PI / 3                              // FOV
277         );
278 
279         overhead_camera->SetName("Overhead Cam");
280         overhead_camera->PushFilter(chrono_types::make_shared<ChFilterRGBA8Access>());
281 
282         // Do we draw a window on the screen?
283         if (cli.GetAsType<bool>("sens_vis"))
284             overhead_camera->PushFilter(chrono_types::make_shared<ChFilterVisualize>(cam_res_width, cam_res_height));
285 
286         // Do we save images to disc?
287         std::string file_path = std::string("SENSOR_OUTPUT/scm") + std::to_string(node_id) + std::string("/");
288         if (cli.GetAsType<bool>("sens_save"))
289             overhead_camera->PushFilter(chrono_types::make_shared<ChFilterSave>(file_path));
290 
291         sensor_manager.AddSensor(overhead_camera);
292     }
293 #endif
294 
295     // ---------------
296     // Simulation loop
297     // ---------------
298 
299     // Inter-module communication data
300     TerrainForces shoe_forces_left(m113.GetVehicle().GetNumTrackShoes(LEFT));
301     TerrainForces shoe_forces_right(m113.GetVehicle().GetNumTrackShoes(RIGHT));
302 
303     // Initialize simulation frame counters
304     int step_number = 0;
305 
306     ChRealtimeStepTimer realtime_timer;
307     ChTimer<> timer;
308     timer.start();
309 
310     while (true) {
311         double time = m113.GetSystem()->GetChTime();
312 
313         // End simulation
314         if (time >= end_time         // ran out of time
315             || !syn_manager.IsOk())  // SynChronoManager has shutdown
316             break;
317 
318 #ifdef CHRONO_IRRLICHT
319         if (app && !app->GetDevice()->run())  //  Irrlicht visualization has stopped
320             break;
321 
322         // Render scene
323         if (app && step_number % render_steps == 0) {
324             app->BeginScene(true, true, irr::video::SColor(255, 140, 161, 192));
325             app->DrawAll();
326             app->EndScene();
327         }
328 #endif
329 
330         // Get driver inputs
331         ChDriver::Inputs driver_inputs = driver.GetInputs();
332 
333         // Update modules (process inputs from other modules)
334         syn_manager.Synchronize(time);  // Synchronize between nodes
335         driver.Synchronize(time);
336         terrain->Synchronize(time);
337         m113.Synchronize(time, driver_inputs, shoe_forces_left, shoe_forces_right);
338 #ifdef CHRONO_IRRLICHT
339         if (app)
340             app->Synchronize("", driver_inputs);
341 #endif
342 
343         // Advance simulation for one timestep for all modules
344         driver.Advance(step_size);
345         terrain->Advance(step_size);
346         m113.Advance(step_size);
347 #ifdef CHRONO_IRRLICHT
348         if (app)
349             app->Advance(step_size);
350 #endif
351 
352 #ifdef CHRONO_SENSOR
353         sensor_manager.Update();
354 #endif
355 
356         // Increment frame number
357         step_number++;
358 
359         if ((int)step_number % 100 == 0 && node_id == 1) {
360             SynLog() << timer.GetTimeSecondsIntermediate() / time << "\n";
361         }
362     }
363     syn_manager.QuitSimulation();
364 
365     return 0;
366 }
367 
AddCommandLineOptions(ChCLI & cli)368 void AddCommandLineOptions(ChCLI& cli) {
369     // Standard demo options
370     cli.AddOption<double>("Simulation", "s,step_size", "Step size", "5e-4");
371     cli.AddOption<double>("Simulation", "e,end_time", "End time", "1000");
372     cli.AddOption<double>("Simulation", "b,heartbeat", "Heartbeat", "1e-2");
373     cli.AddOption<std::string>("Simulation", "c,contact_method", "Contact Method", "SMC", "NSC/SMC");
374 
375     // SCM specific options
376     cli.AddOption<double>("Demo", "d,dpu", "Divisions per unit", "20");
377     cli.AddOption<std::string>("Demo", "t,terrain_type", "Terrain Type", "Flat", "Flat,Hmap");
378 
379     // Many more nodes are impacted with bulldozing and performance is tied to number of deformed nodes, so enabling
380     // this can cause a significant slowdown
381     cli.AddOption<bool>("Demo", "bulldozing", "Toggle bulldozing effects ON", "false");
382 
383     // Visualization is the only reason you should be shy about terrain size. The implementation can easily handle a
384     // practically infinite terrain (provided you don't need to visualize it)
385     cli.AddOption<double>("Demo", "x,sizeX", "Size in the X", "100");
386     cli.AddOption<double>("Demo", "y,sizeY", "Size in the Y", "50");
387 
388     // Irrlicht options
389     cli.AddOption<std::vector<int>>("Irrlicht", "i,irr", "Ranks for irrlicht usage", "-1");
390 
391     // Sensor options
392     cli.AddOption<std::vector<int>>("Sensor", "sens", "Ranks for sensor usage", "-1");
393     cli.AddOption<bool>("Sensor", "sens_save", "Toggle sensor saving ON", "false");
394     cli.AddOption<bool>("Sensor", "sens_vis", "Toggle sensor visualization ON", "false");
395 
396     cli.AddOption<std::vector<int>>("Demo", "r,res", "Camera resolution", "1280,720", "width,height");
397     cli.AddOption<std::vector<double>>("Demo", "c_pos", "Camera Position", "-15,-25", "X,Y");
398 }
399