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