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(¶ms);
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