Tutorial:
demo_fourbar.cpp This tutorial is similar to
the demo_crank example, but here a small user-interface is created, so
that the user can play with a slider to modify the speed of the motor.
The user interface is created with simple GUI features of Irrlicht, but
other kind of interfaces/visualization systems could be used.
Here you can also learn
- how to make a four-bar linkage
- an example of how to plot/fetch data during the simulation.
#include "physics/CHapidll.h" #include "physics/CHsystem.h" #include "irrlicht_interface/CHbodySceneNode.h" #include "irrlicht_interface/CHbodySceneNodeTools.h" #include "irrlicht_interface/CHdisplayTools.h" #include "irrlicht_interface/CHirrWizard.h" #include <irrlicht.h> // Use the namespace of Chrono using namespace chrono; // Use the main namespaces of Irrlicht using namespace irr; using namespace core; using namespace scene; using namespace video; using namespace io; using namespace gui;Some static data: the engine speed. (Using static data is not reccomended in big applications, but this is just a small demo, and makes things easier).
IGUIStaticText* text_enginespeed = 0;The MyEventReceiver class will be used to manage input from the GUI graphical user interface (the interface will be created with the basic -yet flexible- platform independent toolset of Irrlicht). See the Irrlicht documentation for additional informations on how to create and manage user interfaces. In this case, only the slider event is monitored: each time the user plays with the slider, the value is used to modify the speed of the engine, with mengine->Get_spe_funct()->Set_yconst(newspeed); assuming that the engine is already in 'imposed speed mode'.
class MyEventReceiver : public IEventReceiver { public: MyEventReceiver(ChSystem* asystem, IrrlichtDevice *adevice, ChSharedPtr<ChLinkEngine> aengine) { // store pointer to physical system & other stuff so we can tweak them by user keyboard msystem = asystem; mdevice = adevice; mengine = aengine; } bool OnEvent(SEvent event) { // check if user moved the sliders with mouse.. if (event.EventType == EET_GUI_EVENT) { s32 id = event.GUIEvent.Caller->getID(); IGUIEnvironment* env = mdevice->getGUIEnvironment(); switch(event.GUIEvent.EventType) { case EGET_SCROLL_BAR_CHANGED: if (id == 101) // id of 'engine speed' slider.. { s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos(); double newspeed = 10*(double)pos/100.0; // set the speed into engine object mengine->Get_spe_funct()->Set_yconst(newspeed); // show speed as formatted text in interface screen char message[50]; sprintf(message,"Engine speed: %g [rad/s]",newspeed); text_enginespeed->setText(core::stringw(message).c_str()); } break; } } return false; } private: ChSystem* msystem; IrrlichtDevice* mdevice; ChSharedPtr<ChLinkEngine> mengine; };The following is the main function. It begins by creating the Irrlicht device, for the visualization, with proper lights and camera, as usual.
// In CHRONO engine, The DLL_CreateGlobals() - DLL_DeleteGlobals(); pair is needed if // global functions are needed. ChGlobals* GLOBAL_Vars = DLL_CreateGlobals(); // Create the IRRLICHT context (device, etc.) IrrlichtDevice* device = createDevice(video::EDT_DIRECT3D9, core::dimension2d<s32>(640, 480)); if (device == 0) { GetLog() << "Cannot use DirectX - switch to OpenGL \n"; device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(640, 480)); if (!device) return 1; } device->setWindowCaption(L"Example of integration of Chrono::Engine and Irrlicht, with GUI"); // Easy shortcuts to add logo, camera, lights and sky in Irrlicht scene: ChIrrWizard::add_typical_Logo(device); ChIrrWizard::add_typical_Sky(device); ChIrrWizard::add_typical_Lights(device); ChIrrWizard::add_typical_Camera(device);Now create the mechanical system with Chrono::Engine. It consists of a four-bar mechanism, moved by a motorized crank. There will be four rigid bodies (one is used as the truss) and four constraints (one is the engine).
// 1- Create a ChronoENGINE physical system: all bodies and constraints // will be handled by this ChSystem object. ChSystem my_system;Now create the mechanical system with Chrono::Engine. It consists of a four-bar mechanism, moved by a motorized crank. There will be four rigid bodies (one is used as the truss) and four constraints (one is the engine).
my_system.SetIntegration(INT_TASORA); // <-- separate constraint post-stabilization as pos.LCP // 2- Create the rigid bodies of the four-bar mechanical system // (a flywheel, a rod, a rocker, a truss), maybe setting // position/mass/inertias of their center of mass (COG) etc. // ..the truss ChSharedBodyPtr my_body_A(new ChBody); my_system.AddBody(my_body_A); my_body_A->SetBodyFixed(true); // truss does not move! // ..the flywheel ChSharedBodyPtr my_body_B(new ChBody); my_system.AddBody(my_body_B); my_body_B->SetPos(ChVector<>(0,0,0)); // position of COG of flywheel // ..the rod ChSharedBodyPtr my_body_C(new ChBody); my_system.AddBody(my_body_C); my_body_C->SetPos(ChVector<>(4,0,0)); // position of COG of rod // ..the rocker ChSharedBodyPtr my_body_D(new ChBody); my_system.AddBody(my_body_D); my_body_D->SetPos(ChVector<>(8,-4,0)); // position of COG of rod // 3- Create constraints: the mechanical joints between the // rigid bodies. Doesn't matter if some constraints are redundant. // .. an engine between flywheel and truss ChSharedPtr<ChLinkEngine> my_link_AB(new ChLinkEngine); my_link_AB->Initialize(my_body_A, my_body_B, ChCoordsys<>(ChVector<>(0,0,0))); my_link_AB->Set_eng_mode(ChLinkEngine::ENG_MODE_SPEED); my_link_AB->Get_spe_funct()->Set_yconst(CH_C_PI); // speed w=3.145 rad/sec my_system.AddLink(my_link_AB); // .. a revolute joint between flywheel and rod ChSharedPtr<ChLinkLockRevolute> my_link_BC(new ChLinkLockRevolute); my_link_BC->Initialize(my_body_B, my_body_C, ChCoordsys<>(ChVector<>(2,0,0))); my_system.AddLink(my_link_BC); // .. a revolute joint between rod and rocker ChSharedPtr<ChLinkLockRevolute> my_link_CD(new ChLinkLockRevolute); my_link_CD->Initialize(my_body_C, my_body_D, ChCoordsys<>(ChVector<>(8,0,0))); my_system.AddLink(my_link_CD); // .. a revolute joint between rocker and truss ChSharedPtr<ChLinkLockRevolute> my_link_DA(new ChLinkLockRevolute); my_link_DA->Initialize(my_body_D, my_body_A, ChCoordsys<>(ChVector<>(8,-8,0))); my_system.AddLink(my_link_DA);Prepare some graphical-user-interface (GUI) items to show on the screen (mostly, a slider and a text showing the rotation speed of the engine).
// ..add a GUI text and GUI slider to control motor of mechanism via mouse text_enginespeed = device->getGUIEnvironment()->addStaticText( L"Engine speed:", rect<s32>(10,85,150,100), false); IGUIScrollBar* scrollbar = device->getGUIEnvironment()->addScrollBar( true, rect<s32>(10, 105, 150, 120), 0, 101); scrollbar->setMax(100); // ..Finally create the event receiver, for handling all the GUI (user will use // buttons/sliders to modify parameters) MyEventReceiver receiver(&my_system, device, my_link_AB); device->setEventReceiver(&receiver);This is the endless loop for showing the motion of the four-bar mechanism. Note that the mechanism is simply drawn with a wireframe, that is a polygonal line passing through the four corners of the mechanism. Also, a trajectory is drawn on the screen, showing the motion of a point belonging to the rod.
// use this array of points to store trajectory of a rod-point std::vector<chrono::ChVector<>> mtrajectory; while(device->run()) { // Irrlicht must prepare frame to draw device->getVideoDriver()->beginScene(true, true, SColor(255,140,161,192)); // Irrlicht now draws simple lines in 3D world representing a // skeleton of the mechanism, in this instant: // // .. draw items belonging to Irrlicht scene, if any device->getSceneManager()->drawAll(); // .. draw GUI items belonging to Irrlicht screen, if any device->getGUIEnvironment()->drawAll(); // .. draw a grid ChIrrTools::drawGrid(device->getVideoDriver(), 0.5, 0.5); // .. draw a circle representing flywheel ChIrrTools::drawCircle(device->getVideoDriver(), 2.1, ChCoordsys<>(ChVector<>(0,0,0), QUNIT)); // .. draw a small circle representing joint BC ChIrrTools::drawCircle(device->getVideoDriver(), 0.06, ChCoordsys<>(my_link_BC->GetMarker1()->GetAbsCoord().pos, QUNIT)); // .. draw a small circle representing joint CD ChIrrTools::drawCircle(device->getVideoDriver(), 0.06, ChCoordsys<>(my_link_CD->GetMarker1()->GetAbsCoord().pos, QUNIT)); // .. draw a small circle representing joint DA ChIrrTools::drawCircle(device->getVideoDriver(), 0.06, ChCoordsys<>(my_link_DA->GetMarker1()->GetAbsCoord().pos, QUNIT)); // .. draw the rod (from joint BC to joint CD) ChIrrTools::drawSegment(device->getVideoDriver(), my_link_BC->GetMarker1()->GetAbsCoord().pos, my_link_CD->GetMarker1()->GetAbsCoord().pos, video::SColor(255, 0,255,0)); // .. draw the rocker (from joint CD to joint DA) ChIrrTools::drawSegment(device->getVideoDriver(), my_link_CD->GetMarker1()->GetAbsCoord().pos, my_link_DA->GetMarker1()->GetAbsCoord().pos, video::SColor(255, 255,0,0)); // .. draw the trajectory of the rod-point ChIrrTools::drawPolyline(device->getVideoDriver(), mtrajectory, video::SColor(255, 0,150,0)); // HERE CHRONO INTEGRATION IS PERFORMED: THE // TIME OF THE SIMULATION ADVANCES FOR A SINGLE // STEP: my_system.DoStepDynamics(0.01); // We need to add another point to the array of 3d // points describing the trajectory to be drawn.. mtrajectory.push_back(my_body_C->Point_Body2World(&ChVector<>(1,1,0))); // keep only last 150 points.. if (mtrajectory.size()>150) mtrajectory.erase(mtrajectory.begin()); // Irrlicht must finish drawing the frame device->getVideoDriver()->endScene(); }When the while() loop is ended (maybe because the user pressed the 'close window' button), you must delete all Irrlicht items and Chrono::Engine items, before exiting. You do NOT need to delete rigid bodies and links (all things created with smart shared pointers do not need the delete()!). Simply do the following.
// This safely delete every Irrlicht item.. device->getVideoDriver()->drop(); // Remember this at the end of the program, if you started // with DLL_CreateGlobals(); DLL_DeleteGlobals(); return 0; }that's all.