Four-bar linkage example (demo_IRR_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.
  • use a direct solver instead of iterative solver, for highest precision.
// =============================================================================
// Copyright (c) 2014
// 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
// =============================================================================
// Authors: Alessandro Tasora
// =============================================================================
// Demo code about
// - using IRRLICHT as a realtime 3D viewer of a four-bar mechanism
// - using the IRRLICHT graphical user interface (GUI) to handle user-input
// via mouse.
// =============================================================================
#include "chrono/physics/ChLinkMotorRotationSpeed.h"
#include "chrono/physics/ChSystemNSC.h"
#include "chrono_irrlicht/ChIrrApp.h"
// Use the namespaces of Chrono
using namespace chrono;
using namespace chrono::irrlicht;
// Use the main namespaces of Irrlicht
using namespace irr;
using namespace irr::core;
using namespace irr::scene;
using namespace irr::video;
using namespace irr::io;
using namespace irr::gui;
// Some static data (this is a simple application, we can do this ;) just to allow easy GUI manipulation
IGUIStaticText* text_motorspeed = 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).
class MyEventReceiver : public IEventReceiver {
MyEventReceiver(ChSystemNSC* system, IrrlichtDevice* device, std::shared_ptr<ChLinkMotorRotationSpeed> motor) {
// store pointer to physical system & other stuff so we can tweak them by user keyboard
msystem = system;
mdevice = device;
mmotor = motor;
bool OnEvent(const 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) {
if (id == 101) // id of 'motor speed' slider..
s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
double newspeed = 10 * (double)pos / 100.0;
// set the speed into motor object
if (auto mfun = std::dynamic_pointer_cast<ChFunction_Const>(mmotor->GetSpeedFunction()))
// show speed as formatted text in interface screen
char message[50];
sprintf(message, "Motor speed: %g [rad/s]", newspeed);
return false;
ChSystemNSC* msystem;
IrrlichtDevice* mdevice;
std::shared_ptr<ChLinkMotorRotationSpeed> mmotor;
int main(int argc, char* argv[]) {
GetLog() << "Copyright (c) 2017\nChrono version: " << CHRONO_VERSION << "\n\n";
// 1- Create a Chrono physical system
ChSystemNSC my_system;
// Create the Irrlicht visualization (open the Irrlicht device,
// bind a simple user interface, etc. etc.)
ChIrrApp application(&my_system, L"Example of integration of Chrono and Irrlicht, with GUI",
core::dimension2d<u32>(800, 600), false, true);
// Easy shortcuts to add camera, lights, logo and sky in Irrlicht scene:
// 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
auto my_body_A = chrono_types::make_shared<ChBody>();
my_body_A->SetBodyFixed(true); // truss does not move!
// ..the flywheel
auto my_body_B = chrono_types::make_shared<ChBody>();
my_body_B->SetPos(ChVector<>(0, 0, 0)); // position of COG of flywheel
// ..the rod
auto my_body_C = chrono_types::make_shared<ChBody>();
my_body_C->SetPos(ChVector<>(4, 0, 0)); // position of COG of rod
// ..the rocker
auto my_body_D = chrono_types::make_shared<ChBody>();
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.
// .. a motor between flywheel and truss
auto my_link_AB = chrono_types::make_shared<ChLinkMotorRotationSpeed>();
my_link_AB->Initialize(my_body_A, my_body_B, ChFrame<>(ChVector<>(0, 0, 0)));
auto my_speed_function = chrono_types::make_shared<ChFunction_Const>(CH_C_PI); // speed w=3.145 rad/sec
// .. a revolute joint between flywheel and rod
auto my_link_BC = chrono_types::make_shared<ChLinkLockRevolute>();
my_link_BC->Initialize(my_body_B, my_body_C, ChCoordsys<>(ChVector<>(2, 0, 0)));
// .. a revolute joint between rod and rocker
auto my_link_CD = chrono_types::make_shared<ChLinkLockRevolute>();
my_link_CD->Initialize(my_body_C, my_body_D, ChCoordsys<>(ChVector<>(8, 0, 0)));
// .. a revolute joint between rocker and truss
auto my_link_DA = chrono_types::make_shared<ChLinkLockRevolute>();
my_link_DA->Initialize(my_body_D, my_body_A, ChCoordsys<>(ChVector<>(8, -8, 0)));
// Prepare some graphical-user-interface (GUI) items to show
// on the screen
// ..add a GUI text and GUI slider to control motor of mechanism via mouse
text_motorspeed =
application.GetIGUIEnvironment()->addStaticText(L"Motor speed:", rect<s32>(300, 85, 400, 100), false);
IGUIScrollBar* scrollbar =
application.GetIGUIEnvironment()->addScrollBar(true, rect<s32>(300, 105, 450, 120), 0, 101);
// ..Finally create the event receiver, for handling all the GUI (user will use
// buttons/sliders to modify parameters)
MyEventReceiver receiver(&my_system, application.GetDevice(), my_link_AB);
// note how to add the custom event receiver to the default interface:
// Configure the solver with non-default settings
// By default, the solver uses the EULER_IMPLICIT_LINEARIZED stepper, that is very fast,
// but may allow some geometric error in constraints (because it is based on constraint
// stabilization). Alternatively, the timestepper EULER_IMPLICIT_PROJECTED is slower,
// but it is based on constraint projection, so gaps in constraints are less noticeable
// (hence avoids the 'spongy' behaviour of the default stepper, which operates only
// on speed-impulse level and keeps constraints'closed' by a continuous stabilization).
// use this array of points to store trajectory of a rod-point
std::vector<chrono::ChVector<> > mtrajectory;
while (application.GetDevice()->run()) {
// This will draw 3D assets, but in this basic case we will draw some lines later..
// .. draw a grid
ChIrrTools::drawGrid(application.GetVideoDriver(), 0.5, 0.5);
// .. draw a circle representing flywheel
ChIrrTools::drawCircle(application.GetVideoDriver(), 2.1, ChCoordsys<>(ChVector<>(0, 0, 0), QUNIT));
// .. draw a small circle representing joint BC
ChIrrTools::drawCircle(application.GetVideoDriver(), 0.06,
ChCoordsys<>(my_link_BC->GetMarker1()->GetAbsCoord().pos, QUNIT));
// .. draw a small circle representing joint CD
ChIrrTools::drawCircle(application.GetVideoDriver(), 0.06,
ChCoordsys<>(my_link_CD->GetMarker1()->GetAbsCoord().pos, QUNIT));
// .. draw a small circle representing joint DA
ChIrrTools::drawCircle(application.GetVideoDriver(), 0.06,
ChCoordsys<>(my_link_DA->GetMarker1()->GetAbsCoord().pos, QUNIT));
// .. draw the rod (from joint BC to joint CD)
ChIrrTools::drawSegment(application.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(application.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(application.GetVideoDriver(), mtrajectory, video::SColor(255, 0, 150, 0));
// 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)
return 0;
static void drawGrid(irr::video::IVideoDriver *driver, double ustep=0.1, double vstep=0.1, int nu=20, int nv=20, ChCoordsys<> mpos=CSYSNORM, irr::video::SColor mcol=irr::video::SColor(50, 80, 110, 110), bool use_Zbuffer=false)
Easy-to-use function to draw grids in 3D space, with given orientation, color and spacing.
Definition: ChIrrTools.cpp:695
Definition: ChCoordsys.h:38
ChLog & GetLog()
Global function to get the current ChLog object.
Definition: ChLog.cpp:39
virtual void AddLink(std::shared_ptr< ChLinkBase > link)
Attach a link to the underlying assembly.
Definition: ChSystem.h:245
void SetTimestep(double val)
Set/Get the time step for time integration.
Definition: ChIrrAppInterface.cpp:521
static void drawCircle(irr::video::IVideoDriver *driver, double radius, ChCoordsys<> mpos=CSYSNORM, irr::video::SColor mcol=irr::video::SColor(255, 0, 0, 0), int mresolution=36, bool use_Zbuffer=false)
Easy-to-use function to draw a circle line in 3D space, with given color.
Definition: ChIrrTools.cpp:625
ChFrame: a class for coordinate systems in 3D space.
Definition: ChFrame.h:42
Class to add some GUI to Irrlicht+ChronoEngine applications.
Definition: ChIrrApp.h:29
const ChApi ChQuaternion< double > QUNIT
Constant unit quaternion: {1, 0, 0, 0} , corresponds to no rotation (diagonal rotation matrix)
virtual void EndScene()
Call this to end the scene draw at the end of each animation frame.
Definition: ChIrrAppInterface.cpp:562
Namespace with classes for the Irrlicht module.
Definition: ChApiIrr.h:48
static void drawSegment(irr::video::IVideoDriver *driver, ChVector<> mstart, ChVector<> mend, irr::video::SColor mcol=irr::video::SColor(255, 0, 0, 0), bool use_Zbuffer=false)
Easy-to-use function to draw segment lines in 3D space, with given color.
Definition: ChIrrTools.cpp:594
static void drawPolyline(irr::video::IVideoDriver *driver, std::vector< ChVector<> > &mpoints, irr::video::SColor mcol=irr::video::SColor(255, 0, 0, 0), bool use_Zbuffer=false)
Easy-to-use function to draw a polyline in 3D space, given the array of points as a std::vector.
Definition: ChIrrTools.cpp:610
Definition of general purpose 3d vector variables, such as points in 3D.
Definition: ChVector.h:35
virtual void DoStep()
Call this function inside a loop such as.
Definition: ChIrrAppInterface.cpp:572
virtual void AddBody(std::shared_ptr< ChBody > body)
Attach a body to the underlying assembly.
Definition: ChSystem.h:242
virtual void BeginScene(bool backBuffer=true, bool zBuffer=true, irr::video::SColor color=irr::video::SColor(255, 0, 0, 0))
Call this to clean the canvas at the beginning of each animation frame.
Definition: ChIrrAppInterface.cpp:545
Main namespace for the Chrono package.
Definition: ChAsset.cpp:18
void SetTimestepperType(ChTimestepper::Type type)
Set the method for time integration (time stepper type).
Definition: ChSystem.cpp:326
virtual void DrawAll()
Call this function inside a loop such as.
Definition: ChIrrAppInterface.cpp:676
Class for a physical system in which contact is modeled using a non-smooth (complementarity-based) me...
Definition: ChSystemNSC.h:29
void SetUserEventReceiver(irr::IEventReceiver *mreceiver)
Use this function to hook a custom event receiver to the application.
Definition: ChIrrAppInterface.h:129