// ============================================================================= // PROJECT CHRONO - http://projectchrono.org // // Copyright (c) 2014 projectchrono.org // All rights reserved. // // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file at the top level of the distribution and at // http://projectchrono.org/license-chrono.txt. // // ============================================================================= // Author: Wei Hu, Radu Serban // ============================================================================= #include #include #include #include #include #include #include #include "chrono/ChConfig.h" #include "chrono/physics/ChSystemSMC.h" #include "chrono/physics/ChBody.h" #include "chrono/physics/ChInertiaUtils.h" #include "chrono/physics/ChLinkMotorRotationAngle.h" #include "chrono/utils/ChUtilsGeometry.h" #include "chrono/utils/ChUtilsInputOutput.h" #include "chrono/assets/ChTriangleMeshShape.h" #include "chrono/core/ChTimer.h" #include "chrono/physics/ChBodyEasy.h" #include "chrono_fsi/ChSystemFsi.h" #include "chrono_fsi/ChVisualizationFsi.h" #include "chrono_thirdparty/filesystem/path.h" // Chrono namespaces using namespace chrono; using namespace chrono::fsi; using namespace chrono::geometry; // Physical properties of terrain particles double iniSpacing = 0.01; double kernelLength = 0.01; double density = 1700.0; // Dimension of the terrain container double smalldis = 1.0e-9; double bxDim = 1.6 + smalldis; double byDim = 0.33 + smalldis; double bzDim = 0.12 + smalldis; // Size of the wheel double velocity = 0.1; double wheel_radius = 0.135; double wheel_width = 0.165; double wheel_slip = -0.8; // skid // double wheel_AngVel = velocity / wheel_radius; // 1 double wheel_AngVel = velocity / wheel_radius * (1 + wheel_slip); // skid double total_mass = 200; // mass // std::string wheel_obj = "vehicle/hmmwv/hmmwv_tire_coarse_closed.obj"; // Simulation time and stepsize double total_time = 12; // �޸� double dT = 2.5e-4; // linear actuator and angular actuator auto actuator = chrono_types::make_shared(); auto motor = chrono_types::make_shared(); // Save data as csv files to see the results off-line using Paraview bool output = true; int out_fps = 10; // Output directories and settings const std::string out_dir = GetChronoOutputPath() + "FSI_Single_Wheel_Test/"; // Enable/disable run-time visualization (if Chrono::OpenGL is available) bool render = true; float render_fps = 100; // Verbose terminal output bool verbose_fsi = true; bool verbose = true; //------------------------------------------------------------------ // Function to save wheel to Paraview VTK files //------------------------------------------------------------------ void WriteCylinderVTK(const std::string& filename, double radius, double length, const ChFrame<>& frame, unsigned int res) { std::ofstream outf; outf.open(filename, std::ios::app); outf << "# vtk DataFile Version 2.0\nUnstructured Grid Example\nASCII\n\n" << std::endl; outf << "DATASET UNSTRUCTURED_GRID\nPOINTS " << 2 * res << " float\n"; for (int i = 0; i < res; i++) { auto w = frame.TransformPointLocalToParent( ChVector<>(radius * cos(2 * i * 3.1415 / res), -1 * length / 2, radius * sin(2 * i * 3.1415 / res))); outf << w.x() << " " << w.y() << " " << w.z() << "\n"; } for (int i = 0; i < res; i++) { auto w = frame.TransformPointLocalToParent( ChVector<>(radius * cos(2 * i * 3.1415 / res), +1 * length / 2, radius * sin(2 * i * 3.1415 / res))); outf << w.x() << " " << w.y() << " " << w.z() << "\n"; } outf << "CELLS " << res + res << "\t" << 5 * (res + res) << "\n"; for (int i = 0; i < res - 1; i++) { outf << "4 " << i << " " << i + 1 << " " << i + res + 1 << " " << i + res << "\n"; } outf << "4 " << res - 1 << " " << 0 << " " << res << " " << 2 * res - 1 << "\n"; for (int i = 0; i < res / 4; i++) { outf << "4 " << i << " " << i + 1 << " " << +res / 2 - i - 1 << " " << +res / 2 - i << "\n"; } for (int i = 0; i < res / 4; i++) { outf << "4 " << i + res << " " << i + 1 + res << " " << +res / 2 - i - 1 + res << " " << +res / 2 - i + res << "\n"; } outf << "4 " << +res / 2 << " " << 1 + res / 2 << " " << +res - 1 << " " << 0 << "\n"; for (int i = 1; i < res / 4; i++) { outf << "4 " << i + res / 2 << " " << i + 1 + res / 2 << " " << +res / 2 - i - 1 + res / 2 << " " << +res / 2 - i + res / 2 << "\n"; } outf << "4 " << 3 * res / 2 << " " << 1 + 3 * res / 2 << " " << +2 * res - 1 << " " << +res << "\n"; for (int i = 1; i < res / 4; i++) { outf << "4 " << i + 3 * res / 2 << " " << i + 1 + 3 * res / 2 << " " << +2 * res - i - 1 << " " << +2 * res - i << "\n"; } outf << "\nCELL_TYPES " << res + res << "\n"; for (int iele = 0; iele < (res + res); iele++) { outf << "9\n"; } } //------------------------------------------------------------------ // Create the objects of the MBD system. Rigid bodies, and if FSI, // their BCE representation are created and added to the systems //------------------------------------------------------------------ void CreateSolidPhase(ChSystemSMC& sysMBS, ChSystemFsi& sysFSI) { // Common contact material auto cmaterial = chrono_types::make_shared(); cmaterial->SetYoungModulus(1e8); cmaterial->SetFriction(0.9f); cmaterial->SetRestitution(0.4f); cmaterial->SetAdhesion(0); // Create a container -- always FIRST body in the system auto ground = chrono_types::make_shared(); ground->SetIdentifier(-1); ground->SetBodyFixed(true); ground->SetCollide(true); ground->GetCollisionModel()->ClearModel(); chrono::utils::AddBoxContainer(ground, cmaterial, ChFrame<>(), ChVector<>(bxDim, byDim, bzDim), 0.1, ChVector(2, 2, -1)); ground->GetCollisionModel()->BuildModel(); // Add BCE particles attached on the walls into FSI system sysFSI.AddContainerBCE(ground, ChFrame<>(), ChVector<>(bxDim, byDim, 2 * bzDim), ChVector(2, 2, -1)); sysMBS.AddBody(ground); // Create the wheel -- always SECOND body in the system // auto wheel = chrono_types::make_shared(); // double mass = 20; double density = total_mass / (CH_C_PI * wheel_radius * wheel_radius * wheel_width); auto wheel = chrono_types::make_shared(wheel_radius, wheel_width, density, true, true, cmaterial); // Set the general wheel properties ChVector<> gyration = chrono::utils::CalcCylinderGyration(wheel_radius, wheel_width / 2).diagonal(); ChVector<> wheel_IniPos(-bxDim / 2 + 1.2 * wheel_radius, 0.0, wheel_radius + 1.0 * bzDim + 2.0 * iniSpacing); ChVector<> wheel_IniVel(0.0, 0.0, 0.0); wheel->SetMass(total_mass); wheel->SetPos(wheel_IniPos); wheel->SetPos_dt(wheel_IniVel); wheel->SetWvel_loc(ChVector<>(0.0, 0.0, 0.0)); wheel->SetInertiaXX(total_mass * gyration); // // Set the general wheel properties // ChVector<> gyration = chrono::utils::CalcCylinderGyration(wheel_radius, wheel_width / 2).diagonal(); // ChVector<> wheel_IniPos(-bxDim / 2 + 1.2 * wheel_radius, 0.0, wheel_radius + 1.0 * bzDim + 4.0 * iniSpacing); // ChVector<> wheel_IniVel(0.0, 0.0, 0.0); // wheel->SetPos(wheel_IniPos); // wheel->SetPos_dt(wheel_IniVel); // wheel->SetWvel_loc(ChVector<>(0.0, 0.0, 0.0)); // wheel->SetMass(total_mass); // wheel->SetInertiaXX(total_mass * gyration); // // Set the collision // wheel->SetCollide(true); // wheel->SetBodyFixed(false); // wheel->GetCollisionModel()->ClearModel(); // // wheel->GetCollisionModel()->SetSafeMargin(iniSpacing); // // chrono::utils::AddCylinderGeometry(wheel.get(), cmaterial, wheel_radius, wheel_width / 2, VNULL, QUNIT); // wheel->GetCollisionModel()->AddCylinder(cmaterial, wheel_radius, wheel_radius, wheel_width / 2, VNULL, QUNIT); // wheel->GetCollisionModel()->BuildModel(); // Add the wheel to the MBD system sysMBS.AddBody(wheel); // Add the wheel to the FSI system sysFSI.AddFsiBody(wheel); // Add BCE particles attached on the wheel into FSI system sysFSI.AddCylinderBCE(wheel, ChFrame<>(VNULL, Q_from_AngX(CH_C_PI_2)), wheel_radius, wheel_width, true); // Create the chassis -- always THIRD body in the system auto chassis = chrono_types::make_shared(); chassis->SetMass(total_mass * 1.0 / 1.0); chassis->SetPos(wheel->GetPos()); chassis->SetCollide(false); chassis->SetBodyFixed(false); // Add geometry of the chassis. chassis->GetCollisionModel()->ClearModel(); chrono::utils::AddBoxGeometry(chassis.get(), cmaterial, ChVector<>(0.1, 0.1, 0.1), ChVector<>(0, 0, 0)); chassis->GetCollisionModel()->BuildModel(); sysMBS.AddBody(chassis); // Create the axle -- always FOURTH body in the system auto axle = chrono_types::make_shared(); axle->SetMass(total_mass * 1.0 / 1.0); axle->SetPos(wheel->GetPos()); axle->SetCollide(false); axle->SetBodyFixed(false); // Add geometry of the axle. axle->GetCollisionModel()->ClearModel(); chrono::utils::AddSphereGeometry(axle.get(), cmaterial, 0.5, ChVector<>(0, 0, 0)); axle->GetCollisionModel()->BuildModel(); sysMBS.AddBody(axle); // Connect the chassis to the containing bin (ground) through a translational joint and create a linear actuator. auto prismatic1 = chrono_types::make_shared(); prismatic1->Initialize(ground, chassis, ChCoordsys<>(chassis->GetPos(), Q_from_AngY(CH_C_PI_2))); prismatic1->SetName("prismatic_chassis_ground"); sysMBS.AddLink(prismatic1); auto actuator_fun = chrono_types::make_shared(0.0, velocity); actuator->Initialize(ground, chassis, false, ChCoordsys<>(chassis->GetPos(), QUNIT), ChCoordsys<>(chassis->GetPos() + ChVector<>(1, 0, 0), QUNIT)); actuator->SetName("actuator"); actuator->SetDistanceOffset(1); actuator->SetActuatorFunction(actuator_fun); sysMBS.AddLink(actuator); // Connect the axle to the chassis through a vertical translational joint. auto prismatic2 = chrono_types::make_shared(); prismatic2->Initialize(chassis, axle, ChCoordsys<>(chassis->GetPos(), QUNIT)); prismatic2->SetName("prismatic_axle_chassis"); sysMBS.AddLink(prismatic2); // Connect the wheel to the axle through a engine joint. motor->SetName("engine_wheel_axle"); motor->Initialize(wheel, axle, ChFrame<>(wheel->GetPos(), chrono::Q_from_AngAxis(-CH_C_PI / 2.0, ChVector<>(1, 0, 0)))); motor->SetAngleFunction(chrono_types::make_shared(0, wheel_AngVel)); sysMBS.AddLink(motor); } // ============================================================================= int main(int argc, char* argv[]) { // Create oputput directories if (!filesystem::create_directory(filesystem::path(out_dir))) { std::cerr << "Error creating directory " << out_dir << std::endl; return 1; } if (!filesystem::create_directory(filesystem::path(out_dir + "/particles"))) { std::cerr << "Error creating directory " << out_dir + "/particles" << std::endl; return 1; } if (!filesystem::create_directory(filesystem::path(out_dir + "/fsi"))) { std::cerr << "Error creating directory " << out_dir + "/fsi" << std::endl; return 1; } if (!filesystem::create_directory(filesystem::path(out_dir + "/vtk"))) { std::cerr << "Error creating directory " << out_dir + "/vtk" << std::endl; return 1; } // Create the MBS and FSI systems ChSystemSMC sysMBS; ChSystemFsi sysFSI(&sysMBS); ChVector<> gravity = ChVector<>(0, 0, -9.81); sysMBS.Set_G_acc(gravity); sysFSI.Set_G_acc(gravity); sysFSI.SetVerbose(verbose_fsi); // Use the default input file or you may enter your input parameters as a command line argument std::string inputJson = GetChronoDataFile("fsi/input_json/demo_FSI_SingleWheelTest.json"); if (argc == 3) { inputJson = std::string(argv[1]); wheel_slip = std::stod(argv[2]); } else if (argc != 1) { std::cout << "usage: ./demo_FSI_SingleWheelTest " << std::endl; std::cout << "or to use default input parameters ./demo_FSI_SingleWheelTest " << std::endl; return 1; } sysFSI.ReadParametersFromFile(inputJson); // Set the initial particle spacing sysFSI.SetInitialSpacing(iniSpacing); // Set the SPH kernel length sysFSI.SetKernelLength(kernelLength); // Set the terrain density sysFSI.SetDensity(density); // Set the simulation stepsize sysFSI.SetStepSize(dT); // Set the terrain container size sysFSI.SetContainerDim(ChVector<>(bxDim, byDim, bzDim)); // Set SPH discretization type, consistent or inconsistent sysFSI.SetDiscreType(false, false); // Set wall boundary condition sysFSI.SetWallBC(BceVersion::ADAMI); // Set rigid body boundary condition sysFSI.SetRigidBodyBC(BceVersion::ADAMI); // Set cohsion of the granular material sysFSI.SetCohesionForce(1.0e2); // Setup the SPH method sysFSI.SetSPHMethod(FluidDynamics::WCSPH); // Set up the periodic boundary condition (if not, set relative larger values) ChVector<> cMin(-bxDim / 2 * 10, -byDim / 2 - 2 * iniSpacing, -bzDim * 10); ChVector<> cMax(bxDim / 2 * 10, byDim / 2 + 2 * iniSpacing, bzDim * 10); sysFSI.SetBoundaries(cMin, cMax); // ChVector<> cMin = ChVector<>(-bxDim / 2 - iniSpacing / 2, -byDim / 2 - iniSpacing / 2, -5.0 * iniSpacing); // ChVector<> cMax = ChVector<>(bxDim / 2 + iniSpacing / 2, byDim / 2 + iniSpacing / 2, bzDim + 5.0 * iniSpacing); // sysFSI.SetBoundaries(cMin, cMax); // Initialize the SPH particles ChVector<> boxCenter(0.0, 0.0, bzDim / 2); ChVector<> boxHalfDim(bxDim / 2, byDim / 2, bzDim / 2); sysFSI.AddBoxSPH(boxCenter, boxHalfDim); // ChVector<> boxCenter(0, 0, bzDim / 2); // ChVector<> boxHalfDim(bxDim / 2, byDim / 2, bzDim / 2); // std::vector> points = sampler.SampleBox(boxCenter, boxHalfDim); // // Add SPH particles from the sampler points to the FSI system // size_t numPart = (int)points.size(); // double gz = std::abs(sysFSI.Get_G_acc().z()); // for (int i = 0; i < numPart; i++) { // double pre_ini = sysFSI.GetDensity() * gz * (-points[i].z() + bzDim); // double rho_ini = sysFSI.GetDensity() + pre_ini / (sysFSI.GetSoundSpeed() * sysFSI.GetSoundSpeed()); // sysFSI.AddSPHParticle(points[i], rho_ini, pre_ini, sysFSI.GetViscosity(), sysFSI.GetKernelLength(), // ChVector<>(0)); // } // Create Solid region and attach BCE SPH particles CreateSolidPhase(sysMBS, sysFSI); // Set simulation data output length sysFSI.SetOutputLength(0); // Construction of the FSI system must be finalized before running sysFSI.Initialize(); auto wheel = sysMBS.Get_bodylist()[1]; ChVector<> force = actuator->Get_react_force(); ChVector<> torque = motor->Get_react_torque(); ChVector<> w_pos = wheel->GetPos(); ChVector<> w_vel = wheel->GetPos_dt(); ChVector<> angvel = wheel->GetWvel_loc(); // Save wheel mesh // ChTriangleMeshConnected wheel_mesh; // wheel_mesh.LoadWavefrontMesh(GetChronoDataFile(wheel_obj), false, true); // wheel_mesh.RepairDuplicateVertexes(1e-9); // Write the information into a txt file std::ofstream myFile; std::ofstream myDBP_Torque; if (output) { myFile.open(out_dir + "/results.txt", std::ios::trunc); myDBP_Torque.open(out_dir + "/DBP_Torque.txt", std::ios::trunc); } // Create a run-tme visualizer ChVisualizationFsi fsi_vis(&sysFSI); if (render) { fsi_vis.SetTitle("Chrono::FSI single wheel demo"); fsi_vis.SetCameraPosition(ChVector<>(0.8 * bxDim, -3 * byDim, 3 * bzDim), ChVector<>(0, 1, -0.5)); fsi_vis.SetCameraMoveScale(0.05f); fsi_vis.EnableBoundaryMarkers(true); fsi_vis.Initialize(); } // Start the simulation unsigned int output_steps = (unsigned int)round(1 / (out_fps * dT)); unsigned int render_steps = (unsigned int)round(1 / (render_fps * dT)); double time = 0.0; int current_step = 0; ChTimer<> timer; timer.start(); while (time < total_time) { // Get the infomation of the wheel force = actuator->Get_react_force(); torque = motor->Get_react_torque(); w_pos = wheel->GetPos(); w_vel = wheel->GetPos_dt(); angvel = wheel->GetWvel_loc(); if (verbose) { std::cout << "time: " << time << std::endl; std::cout << " wheel position: " << w_pos << std::endl; std::cout << " wheel linear velocity: " << w_vel << std::endl; std::cout << " wheel angular velocity: " << angvel << std::endl; std::cout << " drawbar pull: " << force << std::endl; std::cout << " wheel torque: " << torque << std::endl; } if (output) { myFile << time << "\t" << w_pos.x() << "\t" << w_pos.y() << "\t" << w_pos.z() << "\t" << w_vel.x() << "\t" << w_vel.y() << "\t" << w_vel.z() << "\t" << angvel.x() << "\t" << angvel.y() << "\t" << angvel.z() << "\t" << force.x() << "\t" << force.y() << "\t" << force.z() << "\t" << torque.x() << "\t" << torque.y() << "\t" << torque.z() << "\n"; myDBP_Torque << time << "\t" << force.x() << "\t" << torque.z() << "\n"; } if (output && current_step % output_steps == 0) { std::cout << "-------- Output" << std::endl; sysFSI.PrintParticleToFile(out_dir + "/particles"); sysFSI.PrintFsiInfoToFile(out_dir + "/fsi", time); static int counter = 0; std::string filename = out_dir + "/vtk/wheel." + std::to_string(counter++) + ".vtk"; WriteCylinderVTK(filename, wheel_radius, wheel_width, sysFSI.GetFsiBodies()[0]->GetFrame_REF_to_abs(), 100); } // Render SPH particles if (render && current_step % render_steps == 0) { if (!fsi_vis.Render()) break; } // Call the FSI solver sysFSI.DoStepDynamics_FSI(); time += dT; current_step++; } timer.stop(); std::cout << "\nSimulation time: " << timer() << " seconds\n" << std::endl; if (output) { myFile.close(); myDBP_Torque.close(); } return 0; }