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, Rainer Gericke
13 // =============================================================================
14 //
15 // Demonstration of an OpenCRG terrain and different driver controllers.
16 //
17 // The default world frame is ISO (Z up, X forward, Y to the left).
18 // This demo can be set up to work with a non-ISO frame by uncommenting the line
19 // ChWorldFrame::SetYUP();
20 // at the top of the main function. This will use a world frame with Y up, X
21 // forward, and Z to the right.
22 //
23 // NOTES:
24 // (1) changing the world frame from the ISO default must be done *before* any
25 // other Chrono::Vehicle library calls.
26 // (2) modifications to user code to use a different world frame are minimal:
27 // - set the desired world frame
28 // - properly set vehicle initial position (e.g. initial height above terrain)
29 // - adjust light locations in the run-time visualization system
30 //
31 // =============================================================================
32
33 #include "chrono/physics/ChSystemSMC.h"
34 #include "chrono_vehicle/ChVehicleModelData.h"
35 #include "chrono_vehicle/ChWorldFrame.h"
36 #include "chrono_vehicle/driver/ChPathFollowerDriver.h"
37 #include "chrono_vehicle/driver/ChHumanDriver.h"
38 #include "chrono_vehicle/terrain/CRGTerrain.h"
39 #include "chrono_vehicle/wheeled_vehicle/utils/ChWheeledVehicleIrrApp.h"
40
41 #include "chrono_models/vehicle/hmmwv/HMMWV.h"
42
43 #include "chrono_thirdparty/cxxopts/ChCLI.h"
44 #include "chrono_thirdparty/filesystem/path.h"
45
46 using namespace chrono;
47 using namespace chrono::vehicle;
48 using namespace chrono::vehicle::hmmwv;
49
50 // =============================================================================
51 // Problem parameters
52
53 enum class DriverModelType {
54 PID, // pure PID lateral controller with constant speed controller
55 STANLEY, // geometrical P heading and PID lateral controller with constant speed controller
56 XT, // alternative PID lateral controller with constant speed controller
57 SR, // alternative PID lateral controller with constant speed controller
58 HUMAN // simple realistic human driver
59 };
60
61 // Type of tire model (LUGRE, FIALA, PACEJKA, or TMEASY)
62 TireModelType tire_model = TireModelType::TMEASY;
63
64 // Road visualization (mesh or boundary lines)
65 bool useMesh = false;
66
67 // Desired vehicle speed (m/s)
68 double target_speed = 12;
69
70 // Minimum / maximum speed (m/s) for Human driver type
71 double minimum_speed = 12;
72 double maximum_speed = 30;
73
74 // Simulation step size
75 double step_size = 3e-3;
76 double tire_step_size = 1e-3;
77
78 // Output frame images
79 bool output_images = false;
80 double fps = 60;
81 const std::string out_dir = GetChronoOutputPath() + "OPENCRG_DEMO";
82
DriverModelFromString(const std::string & str)83 DriverModelType DriverModelFromString(const std::string& str) {
84 if (str == "HUMAN")
85 return DriverModelType::HUMAN;
86 if (str == "PID")
87 return DriverModelType::PID;
88 if (str == "STANLEY")
89 return DriverModelType::STANLEY;
90 if (str == "SR")
91 return DriverModelType::SR;
92 if (str == "XT")
93 return DriverModelType::XT;
94 std::cerr << "String \"" + str +
95 "\" does not represent a valid DriverModelType (HUMAN/PID/SR/XT) - returned DriverModelType::HUMAN"
96 << std::endl;
97 return DriverModelType::HUMAN;
98 }
99
100 // =============================================================================
101
102 // Wrapper around a driver system of specified type
103 class MyDriver {
104 public:
MyDriver(DriverModelType type,ChWheeledVehicle & vehicle,std::shared_ptr<ChBezierCurve> path,double road_width,bool path_is_closed)105 MyDriver(DriverModelType type,
106 ChWheeledVehicle& vehicle,
107 std::shared_ptr<ChBezierCurve> path,
108 double road_width,
109 bool path_is_closed)
110 : m_type(type), m_steering_controller(nullptr) {
111 switch (type) {
112 case DriverModelType::PID: {
113 m_driver_type = "PID";
114
115 auto driverPID = chrono_types::make_shared<ChPathFollowerDriver>(vehicle, path, "my_path", target_speed,
116 path_is_closed);
117 driverPID->GetSteeringController().SetLookAheadDistance(5);
118 driverPID->GetSteeringController().SetGains(0.5, 0, 0);
119 driverPID->GetSpeedController().SetGains(0.4, 0, 0);
120
121 m_driver = driverPID;
122 m_steering_controller = &driverPID->GetSteeringController();
123 break;
124 }
125 case DriverModelType::STANLEY: {
126 m_driver_type = "STANLEY";
127
128 auto driverStanley = chrono_types::make_shared<ChPathFollowerDriver>(vehicle, path, "my_path",
129 target_speed, path_is_closed);
130 driverStanley->GetSteeringController().SetLookAheadDistance(5.0);
131 driverStanley->GetSteeringController().SetGains(0.5, 0.0, 0.0);
132 driverStanley->GetSpeedController().SetGains(0.4, 0, 0);
133
134 m_driver = driverStanley;
135 m_steering_controller = &driverStanley->GetSteeringController();
136 break;
137 }
138 case DriverModelType::XT: {
139 m_driver_type = "XT";
140
141 auto driverXT = chrono_types::make_shared<ChPathFollowerDriverXT>(
142 vehicle, path, "my_path", target_speed, path_is_closed, vehicle.GetMaxSteeringAngle());
143 driverXT->GetSteeringController().SetLookAheadDistance(5);
144 driverXT->GetSteeringController().SetGains(0.4, 1, 1, 1);
145 driverXT->GetSpeedController().SetGains(0.4, 0, 0);
146
147 m_driver = driverXT;
148 m_steering_controller = &driverXT->GetSteeringController();
149 break;
150 }
151 case DriverModelType::SR: {
152 m_driver_type = "SR";
153
154 auto driverSR = chrono_types::make_shared<ChPathFollowerDriverSR>(
155 vehicle, path, "my_path", target_speed, path_is_closed, vehicle.GetMaxSteeringAngle(), 3.2);
156 driverSR->GetSteeringController().SetGains(0.1, 5);
157 driverSR->GetSteeringController().SetPreviewTime(0.5);
158 driverSR->GetSpeedController().SetGains(0.4, 0, 0);
159
160 m_driver = driverSR;
161 m_steering_controller = &driverSR->GetSteeringController();
162 break;
163 }
164 case DriverModelType::HUMAN: {
165 m_driver_type = "HUMAN";
166
167 // Driver model read from JSON file
168 ////auto driverHUMAN = chrono_types::make_shared<ChHumanDriver>(
169 //// vehicle::GetDataFile("hmmwv/driver/HumanController.json"), vehicle, path, "my_path",
170 //// path_is_closed, road_width, vehicle.GetMaxSteeringAngle(), 3.2);
171
172 auto driverHUMAN = chrono_types::make_shared<ChHumanDriver>(
173 vehicle, path, "my_path", path_is_closed, road_width, vehicle.GetMaxSteeringAngle(), 3.2);
174 driverHUMAN->SetPreviewTime(0.5);
175 driverHUMAN->SetLateralGains(0.1, 2);
176 driverHUMAN->SetLongitudinalGains(0.1, 0.1, 0.2);
177 driverHUMAN->SetSpeedRange(minimum_speed, maximum_speed);
178
179 m_driver = driverHUMAN;
180 break;
181 }
182 }
183 }
184
GetInputs()185 ChDriver::Inputs GetInputs() { return m_driver->GetInputs(); }
Initialize()186 void Initialize() { m_driver->Initialize(); }
Synchronize(double time)187 void Synchronize(double time) { m_driver->Synchronize(time); }
Advance(double step)188 void Advance(double step) { m_driver->Advance(step); }
189
GetDriverType()190 const std::string& GetDriverType() { return m_driver_type; }
191
GetTargetLocation()192 ChVector<> GetTargetLocation() {
193 if (m_type == DriverModelType::HUMAN)
194 return std::static_pointer_cast<ChHumanDriver>(m_driver)->GetTargetLocation();
195 else
196 return m_steering_controller->GetTargetLocation();
197 }
198
GetSentinelLocation()199 ChVector<> GetSentinelLocation() {
200 if (m_type == DriverModelType::HUMAN)
201 return std::static_pointer_cast<ChHumanDriver>(m_driver)->GetSentinelLocation();
202 else
203 return m_steering_controller->GetSentinelLocation();
204 }
205
PrintStats()206 void PrintStats() {
207 if (m_type != DriverModelType::HUMAN)
208 return;
209
210 auto driverHUMAN = std::static_pointer_cast<ChHumanDriver>(m_driver);
211 std::cout << std::endl;
212 std::cout << "Traveled Distance = " << driverHUMAN->GetTraveledDistance() << " m" << std::endl;
213 std::cout << "Average Speed = " << driverHUMAN->GetAverageSpeed() << " m/s" << std::endl;
214 std::cout << "Maximum Speed = " << driverHUMAN->GetMaxSpeed() << " m/s" << std::endl;
215 std::cout << "Minimum Speed = " << driverHUMAN->GetMinSpeed() << " m/s" << std::endl;
216 std::cout << "Maximum Lateral Acc. = " << driverHUMAN->GetMaxLatAcc() << " m^2/s" << std::endl;
217 std::cout << "Minimum Lateral Acc. = " << driverHUMAN->GetMinLatAcc() << " m^2/s" << std::endl;
218 }
219
220 private:
221 DriverModelType m_type;
222 std::string m_driver_type;
223 std::shared_ptr<ChDriver> m_driver;
224 ChSteeringController* m_steering_controller;
225 };
226
227 // =============================================================================
228
main(int argc,char * argv[])229 int main(int argc, char* argv[]) {
230 GetLog() << "Copyright (c) 2017 projectchrono.org\nChrono version: " << CHRONO_VERSION << "\n\n";
231
232 ChCLI cli(argv[0]);
233
234 // Set up parameter defaults and command-line arguments
235 DriverModelType driver_type = DriverModelType::HUMAN;
236 std::string crg_road_file = "terrain/crg_roads/RoadCourse.crg";
237 bool yup = false;
238
239 cli.AddOption<std::string>("Demo", "m,model", "Controller model type - PID, STANLEY, XT, SR, HUMAN", "HUMAN");
240 cli.AddOption<std::string>("Demo", "f,roadfile", "CRG road filename", crg_road_file);
241 cli.AddOption<bool>("Demo", "y,yup", "Use YUP world frame", std::to_string(yup));
242
243 if (!cli.Parse(argc, argv, true))
244 return 1;
245
246 driver_type = DriverModelFromString(cli.GetAsType<std::string>("model"));
247 crg_road_file = vehicle::GetDataFile(cli.GetAsType<std::string>("roadfile"));
248 yup = cli.GetAsType<bool>("yup");
249
250 // ---------------
251 // Set World Frame
252 // ---------------
253
254 if (yup)
255 ChWorldFrame::SetYUP();
256
257 std::cout << "World Frame\n" << ChWorldFrame::Rotation() << std::endl;
258 std::cout << "Vertical direction: " << ChWorldFrame::Vertical() << std::endl;
259 std::cout << "Forward direction: " << ChWorldFrame::Forward() << std::endl;
260
261 // ----------------------------
262 // Create the containing system
263 // ----------------------------
264
265 ChSystemSMC sys;
266 sys.Set_G_acc(-9.81 * ChWorldFrame::Vertical());
267 sys.SetSolverMaxIterations(150);
268 sys.SetMaxPenetrationRecoverySpeed(4.0);
269
270 // ------------------
271 // Create the terrain
272 // ------------------
273
274 // For a crg terrain with arbitrary start heading the terrain class must be initialized before the vehicle class
275
276 std::cout << std::endl;
277 std::cout << "CRG road file: " << crg_road_file << std::endl;
278
279 CRGTerrain terrain(&sys);
280 terrain.UseMeshVisualization(useMesh);
281 terrain.SetContactFrictionCoefficient(0.8f);
282 terrain.Initialize(crg_road_file);
283
284 // ------------------
285 // Create the vehicle
286 // ------------------
287
288 // Initial location and orientation from CRG terrain (create vehicle 0.5 m above road)
289 auto init_csys = terrain.GetStartPosition();
290 init_csys.pos += 0.5 * ChWorldFrame::Vertical();
291
292 // Create the HMMWV vehicle, set parameters, and initialize
293 HMMWV_Full my_hmmwv(&sys);
294 my_hmmwv.SetContactMethod(ChContactMethod::SMC);
295 my_hmmwv.SetChassisFixed(false);
296 my_hmmwv.SetInitPosition(init_csys);
297 my_hmmwv.SetPowertrainType(PowertrainModelType::SHAFTS);
298 my_hmmwv.SetDriveType(DrivelineTypeWV::RWD);
299 my_hmmwv.SetTireType(tire_model);
300 my_hmmwv.SetTireStepSize(tire_step_size);
301 my_hmmwv.Initialize();
302
303 my_hmmwv.SetChassisVisualizationType(VisualizationType::PRIMITIVES);
304 my_hmmwv.SetSuspensionVisualizationType(VisualizationType::PRIMITIVES);
305 my_hmmwv.SetSteeringVisualizationType(VisualizationType::PRIMITIVES);
306 my_hmmwv.SetWheelVisualizationType(VisualizationType::NONE);
307 my_hmmwv.SetTireVisualizationType(VisualizationType::PRIMITIVES);
308
309 // Get the vehicle path (middle of the road)
310 auto path = terrain.GetRoadCenterLine();
311 bool path_is_closed = terrain.IsPathClosed();
312 double road_length = terrain.GetLength();
313 double road_width = terrain.GetWidth();
314
315 std::cout << "Road length = " << road_length << std::endl;
316 std::cout << "Road width = " << road_width << std::endl;
317 std::cout << std::boolalpha << "Closed loop? " << path_is_closed << std::endl << std::endl;
318
319 // --------------------
320 // Create driver system
321 // --------------------
322
323 MyDriver driver(driver_type, my_hmmwv.GetVehicle(), path, road_width, path_is_closed);
324 driver.Initialize();
325
326 std::cout << "Driver model: " << driver.GetDriverType() << std::endl << std::endl;
327
328 // -------------------------------
329 // Create the visualization system
330 // -------------------------------
331
332 // Light positions (in ISO frame)
333 std::vector<ChVector<>> light_locs = {
334 ChVector<>(-150, -150, 200), //
335 ChVector<>(-150, +150, 200), //
336 ChVector<>(+150, -150, 200), //
337 ChVector<>(+150, +150, 200) //
338 };
339
340 ChWheeledVehicleIrrApp app(&my_hmmwv.GetVehicle(), L"OpenCRG Steering",
341 irr::core::dimension2d<irr::u32>(1000, 800));
342 app.SetHUDLocation(500, 20);
343 app.SetSkyBox();
344 app.AddTypicalLogo();
345 for (auto& loc : light_locs)
346 app.AddLight(irr::core::vector3dfCH(ChWorldFrame::FromISO(loc)), 100);
347 app.SetChaseCamera(ChVector<>(0.0, 0.0, 1.75), 6.0, 0.5);
348 app.SetTimestep(step_size);
349
350 // Visualization of controller points (sentinel & target)
351 irr::scene::IMeshSceneNode* ballS = app.GetSceneManager()->addSphereSceneNode(0.1f);
352 irr::scene::IMeshSceneNode* ballT = app.GetSceneManager()->addSphereSceneNode(0.1f);
353 ballS->getMaterial(0).EmissiveColor = irr::video::SColor(0, 255, 0, 0);
354 ballT->getMaterial(0).EmissiveColor = irr::video::SColor(0, 0, 255, 0);
355
356 // Finalize construction of visualization assets
357 app.AssetBindAll();
358 app.AssetUpdateAll();
359
360 // ----------------
361 // Output directory
362 // ----------------
363
364 if (output_images) {
365 if (!filesystem::create_directory(filesystem::path(out_dir))) {
366 std::cout << "Error creating directory " << out_dir << std::endl;
367 return 1;
368 }
369 }
370
371 // ---------------
372 // Simulation loop
373 // ---------------
374
375 // Number of simulation steps between image outputs
376 double render_step_size = 1 / fps;
377 int render_steps = (int)std::ceil(render_step_size / step_size);
378
379 // Initialize frame counters
380 int sim_frame = 0;
381 int render_frame = 0;
382
383 while (app.GetDevice()->run()) {
384 double time = my_hmmwv.GetSystem()->GetChTime();
385
386 // Driver inputs
387 ChDriver::Inputs driver_inputs = driver.GetInputs();
388
389 // Update sentinel and target location markers for the path-follower controller.
390 ballS->setPosition(irr::core::vector3dfCH(driver.GetSentinelLocation()));
391 ballT->setPosition(irr::core::vector3dfCH(driver.GetTargetLocation()));
392
393 // Render scene and output images
394 app.BeginScene(true, true, irr::video::SColor(255, 140, 161, 192));
395 app.DrawAll();
396
397 // Draw the world reference frame at the sentinel location
398 app.RenderFrame(ChFrame<>(driver.GetSentinelLocation()));
399
400 if (output_images && sim_frame % render_steps == 0) {
401 char filename[200];
402 sprintf(filename, "%s/image_%05d.bmp", out_dir.c_str(), render_frame);
403 app.WriteImageToFile(filename);
404 render_frame++;
405 }
406
407 // Update modules (process inputs from other modules)
408 driver.Synchronize(time);
409 terrain.Synchronize(time);
410 my_hmmwv.Synchronize(time, driver_inputs, terrain);
411 app.Synchronize(driver.GetDriverType(), driver_inputs);
412
413 // Advance simulation for one timestep for all modules
414 driver.Advance(step_size);
415 terrain.Advance(step_size);
416 my_hmmwv.Advance(step_size);
417 app.Advance(step_size);
418 sys.DoStepDynamics(step_size);
419
420 // Increment simulation frame number
421 sim_frame++;
422
423 app.EndScene();
424 }
425
426 driver.PrintStats();
427
428 return 0;
429 }
430