pendulum Tutorial: demo_pendulum.cpp

Create some swinging pendulums, anchored to sliding joints. They move when the fan pushes some air toward them.

This tutorial shows how to:

- create a pendulum
- apply custom forces using 'force accumulators' (the simplified aereodinamic drag, in this case)
- create constraints with upper-lower limits (the horizontal sliding joints between pendula and truss)

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

The following function will be used to apply forces caused by a rotating fan, to all objects in front of it (a simple example just to demonstrate how to apply custom forces: the aereodynamical drag is a very simplified model).


void apply_fan_force (	ChSystem* msystem,		// contains all bodies
						ChCoordsys<>& fan_csys, // pos and rotation of fan 
						double aradius,			// radius of fan
						double aspeed,			// speed of fan
						double adensity)		// density (heuristic)
{
	for (unsigned int i=0; i<msystem->Get_bodylist()->size(); i++)
	{
		ChBody* abody = (*msystem->Get_bodylist())[i];

		// Remember to reset 'user forces accumulators':
		abody->Empty_forces_accumulators();

		// initialize speed of air (steady, if outside fan stream): 
		ChVector<> abs_wind(0,0,0);

		// calculate the position of body COG in fan coordinates:
		ChVector<> mrelpos = fan_csys.TrasformParentToLocal(abody->GetPos());
		ChVector<> mrelpos_ondisc = mrelpos; mrelpos_ondisc.z=0;
		
		if (mrelpos.z >0) // if not behind fan..
			if (mrelpos_ondisc.Length() < aradius)	
			{
				//OK! we are inside wind stream cylinder..
				// wind is directed as normal to the fan disc
				abs_wind = fan_csys.TrasformLocalToParent(ChVector<>(0,0,1));
				// wind inside fan stream is constant speed 
				abs_wind *= -aspeed;
			}

		// force proportional to relative speed body-wind 
		// and fluid density (NOTE! pretty simplified physics..)
		ChVector<> abs_force = ( abs_wind - abody->GetPos_dt() ) * adensity;
		// apply this force at the body COG
		abody->Accumulate_force(abs_force, abody->GetPos(), false);
	}
}

The following is the main function, where - as usual - the Irrlicht device is created, the Chrono::Engine globals are initialized, and the real-time simulation loop is performed. See the previous tutorials for more explainations.



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

	// 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");

	IVideoDriver*	 driver        = device->getVideoDriver();
	ISceneManager*	 msceneManager = device->getSceneManager();
	IGUIEnvironment* guienv        = device->getGUIEnvironment();
	timer = device->getTimer(); // timer declared in CHbodySceneNode.h
 
	// 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,14,-20));

   	// create text with info
	IGUIStaticText* textFPS = guienv->addStaticText(L"FPS", rect<s32>(10,80,120,220), true);


 
    // 
	// HERE YOU CREATE THE MECHANICAL SYSTEM OF CHRONO...
	// 


	// Create a ChronoENGINE physical system!
	ChSystem my_system;


	
	//
	// Create all the rigid bodies!!!!
	//
 
	// ..create the five pendulums (bodies are Irrlicht nodes of
	//   the special class ChBodySceneNode, which encapsulate ChBody items ): 

	for (int k=0;k<5; k++)
	{
		double z_step =(double)k*2.;

		 // .. the truss
		ChBodySceneNode* mrigidBody0 = (ChBodySceneNode*)addChBodySceneNode_easyBox(
								&my_system, msceneManager,
								1.0,
								ChVector<>(0,0,z_step),
								ChQuaternion<>(1,0,0,0), 
								ChVector<>(5,1,0.5) ); 
		mrigidBody0->GetBody()->SetBodyFixed(true); // the truss does not move!
		mrigidBody0->GetBody()->SetCollide(false);

		ChBodySceneNode* mrigidBody1 = (ChBodySceneNode*)addChBodySceneNode_easyBox(
								&my_system, msceneManager,
								1.0,
								ChVector<>(0,-3,z_step),
								ChQuaternion<>(1,0,0,0), 
								ChVector<>(1,6,1) );
		mrigidBody1->GetBody()->SetCollide(false);

		ChBodySceneNode* mrigidBody2 = (ChBodySceneNode*)addChBodySceneNode_easyBox(
								&my_system, msceneManager,
								1.0,
								ChVector<>(3,-6,z_step),
								ChQuaternion<>(1,0,0,0), 
								ChVector<>(6,1,1) );
		mrigidBody2->GetBody()->SetCollide(false);

		ChBodySceneNode* mrigidBody3 = (ChBodySceneNode*)addChBodySceneNode_easyBox(
								&my_system, msceneManager,
								1.0,
								ChVector<>(6,-9,z_step),
								ChQuaternion<>(1,0,0,0), 
								ChVector<>(1,6,1) );
		mrigidBody3->GetBody()->SetCollide(false);
 
		//
		// Create the links between bodies!!!!
		// 
  
		 // .. a joint of type 'point on a line', with upper and lower limits on
		 //    the X sliding direction, for the pendulum-ground constraint.
		ChSharedPtr<ChLinkLockPointLine>  my_link_01(new ChLinkLockPointLine);
		my_link_01->Initialize(mrigidBody1->GetBody(), 
							   mrigidBody0->GetBody(), 
							   ChCoordsys<>(ChVector<>(0,0,z_step)));
		my_link_01->GetLimit_X()->Set_active(true);
		my_link_01->GetLimit_X()->Set_max( 1.0);
		my_link_01->GetLimit_X()->Set_min(-1.0);
		my_system.AddLink(my_link_01);

		// .. a spherical joint
		ChSharedPtr<ChLinkLockSpherical>  my_link_12(new ChLinkLockSpherical);
		my_link_12->Initialize(mrigidBody2->GetBody(), 
							   mrigidBody1->GetBody(), 
							   ChCoordsys<>(ChVector<>(0,-6,z_step)));
		my_system.AddLink(my_link_12);

		// .. a spherical joint
		ChSharedPtr<ChLinkLockSpherical>  my_link_23(new ChLinkLockSpherical);
		my_link_23->Initialize(mrigidBody3->GetBody(), 
							   mrigidBody2->GetBody(), 
							   ChCoordsys<>(ChVector<>(6,-6,z_step)));
		my_system.AddLink(my_link_23);

	}
	
  
	//
	// THE SOFT-REAL-TIME CYCLE
	//

	// create a 'fan ventilator' object, using Irrlicht mesh
	// loading and handling (this object is here for aesthetical reasons,
	// it is NOT handled by Chrono::Engine).
	double fan_radius = 5.3;
	IAnimatedMesh*	fanMesh = msceneManager->getMesh("../data/fan2.obj");
	IAnimatedMeshSceneNode* fanNode = msceneManager->addAnimatedMeshSceneNode (fanMesh);
	fanNode->setScale(irr::core::vector3df((irr::f32)fan_radius,(irr::f32)fan_radius,(irr::f32)fan_radius));
 
	// This will help choosing an integration step which matches the
	// real-time step of the simulation..
	ChRealtimeStepTimer m_realtime_timer;

	while(device->run())
	{
		// Irrlicht must prepare frame to draw
		driver->beginScene(true, true, SColor(255,140,161,192));

		// Irrlicht draws all 3D objects and all GUI items
		msceneManager->drawAll();
		guienv->drawAll(); 
 
		// draw a grid on the horizontal XZ plane
		ChIrrTools::drawGrid(driver, 2, 2, 20,20, 
			ChCoordsys<>(ChVector<>(0,-20,0), Q_from_AngX(CH_C_PI_2) ),
			video::SColor(255, 80,100,100), true);

		// Update the position of the spinning fan (an Irrlicht
		// node, which is here just for aesthetical reasons!)
		ChQuaternion<> my_fan_rotation; 
		my_fan_rotation.Q_from_AngY(my_system.GetChTime()*-0.5); 
		ChQuaternion<> my_fan_spin;
		my_fan_spin.Q_from_AngZ(my_system.GetChTime()*4);
		ChCoordsys<> my_fan_coord(
						ChVector<>(12, -6, 0),
						my_fan_rotation); 
		ChFrame<> my_fan_framerotation(my_fan_coord);
		ChFrame<> my_fan_framespin(ChCoordsys<>(VNULL, my_fan_spin));
		ChIrrTools::alignIrrlichtNodeToChronoCsys(fanNode, 
						(my_fan_framespin >> my_fan_framerotation).GetCoord());

		// Apply forces caused by fan & wind if Chrono rigid bodies are
		// in front of the fan, using a simple tutorial function (see above):
		apply_fan_force(&my_system, my_fan_coord, fan_radius, 2.2, 0.5);

		// HERE CHRONO INTEGRATION IS PERFORMED: THE 
		// TIME OF THE SIMULATION ADVANCES FOR A SINGLE
		// STEP: (2x as fast as real-time - looks better:)
		my_system.DoStepDynamics( 2* m_realtime_timer.SuggestSimulationStep(0.01) );

 
		driver->endScene(); 

	}

	// This Irrlicht statement will safely delete all..
	device->drop();
 


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

	return 0;
}


that's all.