ChronoEngine:Demo crank

From ChronoWiki

(Redirected from Demo crank)
Jump to: navigation, search

The simpliest way to integrate Chrono::Engine in the Irrlicht 3D visualization library: in fact the coordinates of the joints are simply used as end-points of simple polygonal lines which are drawn in the 3D space for each frame redraw, to show a very simplified 'skeleton' of a slider-crank mechanism.

Let's start

First of all, include some headers needed for this example, use required namespaces, initialize the library, etc:

#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 "core/CHrealtimeStep.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;



int main(int argc, char* argv[])
{

	// In CHRONO engine, The DLL_CreateGlobals() - DLL_DeleteGlobals();
	// pair is needed if global functions are needed.
	DLL_CreateGlobals();

Now remember that you are going to use Irrlicht, so you need to initialize also the Irrlicht device:

// Create the IRRLICHT context (device, etc.)
	IrrlichtDevice* device = createDevice(video::EDT_DIRECT3D9,
				core::dimension2d<u32>(640, 480),
				24, false, false, true);
	if (device == 0)
	{
		GetLog() << "Cannot use DirectX - switch to OpenGL \n";
		device = createDevice(video::EDT_OPENGL,
					core::dimension2d<u32>(640, 480));
		if (!device) return 1;
	}

	device->setWindowCaption(L"SIMPLIEST example of Chrono::Engine and Irrlicht");

	IVideoDriver* driver           = device->getVideoDriver();
	ISceneManager*	 msceneManager = device->getSceneManager();
	IGUIEnvironment* guienv        = device->getGUIEnvironment();

Thank to the functions contained in the ChIrrWizard class, you can easily create a Chrono::Engine logo on the top left corner of the window, a sky dome with 'smooth horizon effect', two default lights and a camera looking forward.

// 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, core::vector3df(0,0,-6));

Now comes the important part of the tutorial: how to build the mechanical system. First of all, create the physical system:

ChSystem my_system;

Create the three rigid bodies of the slider-crank mechanical system (a crank, a rod, a truss), maybe setting position/mass/inertias of their center of mass (COG) etc. Remember that each body must be added to the physical system via the AddBody() function, or generic Add() function.

// ..the truss
	ChSharedBodyPtr  my_body_A(new ChBody);
	my_system.AddBody(my_body_A);
	my_body_A->SetBodyFixed(true);			// truss does not move!

	// ..the crank
	ChSharedBodyPtr  my_body_B(new ChBody);
	my_system.AddBody(my_body_B);
	my_body_B->SetPos(ChVector<>(1,0,0));	// position of COG of crank

	// ..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

Create the three constraints (the mechanical joints between the rigid bodies). Note that the engine is a particular kind of constraint as well (an object of class ChLinkEngine). It can work in different ways: imposing speed, imposing rotation as a function of time, imposing torque, etc. In this case, we use the 'impose speed' mode, with speed=constant.

// .. a revolute joint between crank 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 slider joint between rod and truss
	ChSharedPtr<ChLinkLockPointLine> my_link_CA(new ChLinkLockPointLine);
	my_link_CA->Initialize(my_body_C, my_body_A, ChCoordsys<>(ChVector<>(6,0,0)));
	my_system.AddLink(my_link_CA);

	// .. an engine between crank 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);

Ok, all the items have been added to the physical system. We can show the simulation. Since the simulation is shown in the Irrlicht 3D display, we create an endless loop, which continuosly redraws the 3D window. For each frame in the loop, we need to call some Irrlicht functions: beginScene() is used to clean the display by drawing the background and resetting for the next redraw, then drawAll() will draw all 3D objects, if any, then we call drawGrid() to draw a 3D reference grid, then we call multiple times the drawSegment() function to paint the polygonal line which represent the 'skeleton' of the moving slider-crank. Also the drawCircle() function is used to create the schematic representation of a revolute joint and of the flywheel.

// This will help choosing an integration step which matches the
	// real-time step of the simulation..
	ChRealtimeStepTimer m_realtime_timer;

	bool removed = false;

	while(device->run())
	{
		// Irrlicht must prepare frame to draw
		driver->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
		msceneManager->drawAll();
		// .. draw GUI items belonging to Irrlicht screen, if any
		guienv->drawAll();
		// .. draw a grid
		ChIrrTools::drawGrid(driver, 0.5, 0.5);
		// .. draw the rod (from joint BC to joint CA)
		ChIrrTools::drawSegment(driver,
			my_link_BC->GetMarker1()->GetAbsCoord().pos,
			my_link_CA->GetMarker1()->GetAbsCoord().pos,
			video::SColor(255,   0,255,0));
		// .. draw the crank (from joint AB to joint BC)
		ChIrrTools::drawSegment(driver,
			my_link_AB->GetMarker1()->GetAbsCoord().pos,
			my_link_BC->GetMarker1()->GetAbsCoord().pos,
			video::SColor(255, 255,0,0));
		// .. draw a small circle at crank origin
		ChIrrTools::drawCircle(driver, 0.1, ChCoordsys<>(ChVector<>(0,0,0), QUNIT));

		// HERE CHRONO INTEGRATION IS PERFORMED: THE
		// TIME OF THE SIMULATION ADVANCES FOR A SINGLE
		// STEP:


		my_system.DoStepDynamics( m_realtime_timer.SuggestSimulationStep(0.02) );

		// Irrlicht must finish drawing the frame
		driver->endScene();

	}

Note! The simulation is advanced by the DoStepDynamics() function, which takes the time step as an argument. In this example, this function is called each time a new frame is displayed.

The interesting issue here, is that if you had simply used DoStepDynamics(0.01), for example, you would have seen the pendulum going faster on recent CPU, and going slower on less performant CPU (because maybe a fast computer can compute DoStepDynamics() and display the 3D view very quickly, say at 500fps, while the simulated mechanism represents something which advances 0.01s per frame... This would mean that the CPU would run 5 times faster than real-time, and the fast-motion on the pendulum won't be very nice. To avoid this problem, and to force Chrono::Engine to advance the 'right amount of time step' for each frame update, you can use the ChRealtimeStepTimer object, as in this example, above. In fact this tool is able to tell 'how much the simulation should advance' in order to keep it at the same pace of the real-time display update. That is, using the SuggestSimulationStep() function you get the proper dt for the DoStepDynamics() function, and the pace of the simulation should be about realtime (and the same on computer with different CPU speed). However, remember that if the computer is slow (or the simulation is complex) maybe that the suggested timestep could be too large, causing unstable and inprecise simulaitons - for this reason, the SuggestSimulationStep() can use a parameter (we used 0.02 in our example) which enforce an upper limit on the duration of the suggested step.

To finish with Irrlicht and Chrono::Engine libraries, you simply do the following:

// This safely delete every Irrlicht item..
	device->drop();



	// Remember this at the end of the program, if you started
	// with DLL_CreateGlobals();
	DLL_DeleteGlobals();

	return 0;
}
Personal tools