Mecanum omnidirectional robot example (demo_MBS_mecanum.cpp)

Simulate a small mobile robot with omnidirectional 'mecanum' wheels.

Use keyboard to perform basic motion.

This tutorial shows how to:

• make complex models, with 'barrel' collision shapes.
• use the keyboard in Irrlicht 3D view, to interact with the system in realtime.
• manage rotations of references using ChCoordsys and ChFrame classes.
// =============================================================================
// PROJECT CHRONO - http://projectchrono.org
//
//
// 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
// =============================================================================
//
// - collisions and contacts
// - using the 'barrel' shape to create rollers for building omnidirectional
// wheels in a mobile robot.
//
// =============================================================================
#include "chrono/core/ChRealtimeStep.h"
#include "chrono/physics/ChSystemNSC.h"
#include "chrono/physics/ChBodyEasy.h"
#include "chrono/utils/ChUtilsGeometry.h"
#include "chrono/assets/ChVisualShapeBarrel.h"
#include "chrono_irrlicht/ChVisualSystemIrrlicht.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;
double STATIC_rot_speed = 0;
double STATIC_x_speed = 0;
double STATIC_z_speed = 0;
float STATIC_wheelfriction = 0.6f;
#define MAX_ROT_SPEED 0.8
#define MAX_XZ_SPEED 10
public:
bool OnEvent(const SEvent& event) {
// check if user presses keys
if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) {
switch (event.KeyInput.Key) {
case irr::KEY_KEY_Q:
STATIC_x_speed += 1.5;
if (STATIC_x_speed > MAX_XZ_SPEED)
STATIC_x_speed = MAX_XZ_SPEED;
return true;
case irr::KEY_KEY_W:
STATIC_x_speed -= 1.5;
if (STATIC_x_speed < -MAX_XZ_SPEED)
STATIC_x_speed = -MAX_XZ_SPEED;
return true;
case irr::KEY_KEY_A:
STATIC_z_speed += 1.5;
if (STATIC_z_speed > MAX_XZ_SPEED)
STATIC_z_speed = MAX_XZ_SPEED;
return true;
case irr::KEY_KEY_Z:
STATIC_z_speed -= 1.5;
if (STATIC_z_speed < -MAX_XZ_SPEED)
STATIC_z_speed = -MAX_XZ_SPEED;
return true;
case irr::KEY_KEY_E:
STATIC_rot_speed += 0.05;
if (STATIC_rot_speed > MAX_ROT_SPEED)
STATIC_rot_speed = MAX_ROT_SPEED;
return true;
case irr::KEY_KEY_R:
STATIC_rot_speed -= 0.05;
if (STATIC_rot_speed < -MAX_ROT_SPEED)
STATIC_rot_speed = -MAX_ROT_SPEED;
return true;
default:
break;
}
}
return false;
}
};
// This small function creates a Mecanum wheel, made with many ChBodySceneNode rigid bodies (a central
// wheel and the many radial rollers, already lined to the wheel with revolute joints.)
// The function returns the pointer to the central wheel.
std::shared_ptr<ChBody> create_mecanum_wheel(ChSystemNSC& sys,
ChVector3d shaft_position,
ChQuaternion<> shaft_alignment,
double wheel_width,
int n_rollers,
double roller_angle,
double roller_density,
double spindle_density) {
ChFrameMoving<> ftot(shaft_position, shaft_alignment); // will be used to transform pos & rot of all objects
auto centralWheel = chrono_types::make_shared<ChBodyEasyCylinder>(ChAxis::Y, //
spindle_density, // density
true, // visualize
false); // no collision
centralWheel->SetPos(shaft_position);
centralWheel->SetRot(shaft_alignment);
centralWheel->GetVisualShape(0)->SetTexture(GetChronoDataFile("textures/pinkwhite.png"));
auto wheel_mat = chrono_types::make_shared<ChContactMaterialNSC>();
wheel_mat->SetFriction(STATIC_wheelfriction);
double half_length_roller = 0.5 * wheel_width * 1.0 / (cos(roller_angle));
for (int iroller = 0; iroller < n_rollers; iroller++) {
double pitch = CH_2PI * ((double)iroller / (double)n_rollers);
// Create the roller
auto roller = chrono_types::make_shared<ChBody>();
// move it to slanted aligment
ChFrameMoving<> f3 = f1 >> f2 >> ftot;
// approximate mass & inertia to a cylinder:
roller->SetMass( //
ChCylinder::GetVolume(roller_elliptical_rad_Hor + Roffset, 2 * half_length_roller) * roller_density);
roller->SetInertia( //
ChCylinder::GetGyration(roller_elliptical_rad_Hor + Roffset, 2 * half_length_roller) * roller_density);
auto shape = chrono_types::make_shared<ChCollisionShapeBarrel>(wheel_mat, //
-half_length_roller, +half_length_roller, //
Roffset);
roller->EnableCollision(true);
auto rollershape = chrono_types::make_shared<ChVisualShapeBarrel>(-half_length_roller, +half_length_roller, //
Roffset);
// Make the revolute joint between the roller and the central wheel
// (preconcatenate rotation 90 degrees on X, to set axis of revolute joint)
ChFrameMoving<> fr(ChVector3d(0, 0, 0), QuatFromAngleX(CH_PI_2));
ChFrameMoving<> frabs = fr >> f3;
}
return centralWheel;
}
int main(int argc, char* argv[]) {
std::cout << "Copyright (c) 2017 projectchrono.org\nChrono version: " << CHRONO_VERSION << std::endl;
// Create a ChronoENGINE physical system
double roller_angle = CH_PI / 4;
// Create the robot truss, as a circular platform
auto platform = chrono_types::make_shared<ChBodyEasyCylinder>(ChAxis::Y, //
1000, // density
true, // visualize
false); // no collision
// ChCollisionModel::SetDefaultSuggestedEnvelope(0.01);
// ChCollisionModel::SetDefaultSuggestedMargin(0.005);
// create the wheels and link them to the platform
ChFrame<> f0(ChVector3d(0, 0, 0), QuatFromAngleX(CH_PI_2));
ChFrame<> f2_wA(VNULL, QuatFromAngleY(0 * (CH_2PI / 3.0)));
ChFrame<> f2_wB(VNULL, QuatFromAngleY(1 * (CH_2PI / 3.0)));
ChFrame<> f2_wC(VNULL, QuatFromAngleY(2 * (CH_2PI / 3.0)));
ChFrame<> ftot_wA = f0 >> f1 >> f2_wA;
ChFrame<> ftot_wB = f0 >> f1 >> f2_wB;
ChFrame<> ftot_wC = f0 >> f1 >> f2_wC;
auto spindle_A = create_mecanum_wheel(sys,
ftot_wA.GetCoordsys().pos, // wheel position
ftot_wA.GetCoordsys().rot, // wheel alignment
2.2, // wheel width
8, // n. of rollers
roller_angle, // angle of rollers
0.65, // max rad. of roller
1000, // density of roller
1000); // density of the spindle
auto spindle_B = create_mecanum_wheel(sys,
ftot_wB.GetCoordsys().pos, // wheel position
ftot_wB.GetCoordsys().rot, // wheel alignment
2.2, // wheel width
8, // n. of rollers
roller_angle, // angle of rollers
0.65, // max rad. of roller
1000, // density of roller
1000); // density of the spindle
auto spindle_C = create_mecanum_wheel(sys,
ftot_wC.GetCoordsys().pos, // wheel position
ftot_wC.GetCoordsys().rot, // wheel alignment
2.2, // wheel width
8, // n. of rollers
roller_angle, // angle of rollers
0.65, // max rad. of roller
1000, // density of roller
1000); // density of the spindle
// Create the ground for the collision
auto ground_mat = chrono_types::make_shared<ChContactMaterialNSC>();
ground_mat->SetFriction(STATIC_wheelfriction);
auto ground = chrono_types::make_shared<ChBodyEasyBox>(200, 1, 200, // size
1000, // density
true, // visualize
true, // collide
ground_mat); // contact material
ground->SetPos(ChVector3d(0, -5, 0));
ground->SetFixed(true);
ground->GetVisualShape(0)->SetTexture(GetChronoDataFile("textures/concrete.jpg"), 100, 100);
// Create the Irrlicht visualization system
auto vis = chrono_types::make_shared<ChVisualSystemIrrlicht>();
vis->AttachSystem(&sys);
vis->SetWindowSize(800, 600);
vis->SetWindowTitle("Mecanum robot simulator");
vis->Initialize();
vis->GetGUIEnvironment()->addStaticText(L"Use keys Q,W, A,Z, E,R to move the robot", rect<s32>(150, 10, 430, 40),
true);
// Prepare the physical system for the simulation
sys.SetTimestepperType(ChTimestepper::Type::EULER_IMPLICIT_PROJECTED);
sys.GetSolver()->AsIterative()->SetMaxIterations(30);
// Simulation loop
double timestep = 0.01;
ChRealtimeStepTimer realtime_timer;
while (vis->Run()) {
vis->BeginScene();
vis->Render();
vis->EndScene();
// ADVANCE THE SIMULATION FOR ONE TIMESTEP
sys.DoStepDynamics(timestep);
// change motor speeds depending on user setpoints from GUI
ChVector3d imposed_speed(STATIC_x_speed, 0, STATIC_z_speed);
ChFrame<> abs_roll_wA = roll_twist >> f2_wA >> ChFrame<>(platform->GetCoordsys());
double wheel_A_rotspeed =
((abs_roll_wA.GetRotMat().transpose() * imposed_speed).x() / sin(roller_angle)) / wheel_radius;
ChFrame<> abs_roll_wB = roll_twist >> f2_wB >> ChFrame<>(platform->GetCoordsys());
double wheel_B_rotspeed =
((abs_roll_wB.GetRotMat().transpose() * imposed_speed).x() / sin(roller_angle)) / wheel_radius;
ChFrame<> abs_roll_wC = roll_twist >> f2_wC >> ChFrame<>(platform->GetCoordsys());
double wheel_C_rotspeed =
((abs_roll_wC.GetRotMat().transpose() * imposed_speed).x() / sin(roller_angle)) / wheel_radius;
fun->SetConstant(wheel_A_rotspeed);
fun->SetConstant(wheel_B_rotspeed);
fun->SetConstant(wheel_C_rotspeed);
realtime_timer.Spin(timestep);
}
return 0;
}
std::string GetChronoDataFile(const std::string &filename)
Get the full path to the specified filename, given relative to the Chrono data directory (thread safe...
Definition: ChGlobal.cpp:37
ChQuaterniond QuatFromAngleX(double angle)
Convert from a rotation about X axis to a quaternion.
Definition: ChRotation.cpp:188
virtual double GetVolume() const override
Return the volume of this solid.
Definition: ChCylinder.cpp:36
Attach an arbitrary ChPhysicsItem (e.g.
Definition: ChSystem.cpp:196
virtual std::shared_ptr< ChSolver > GetSolver()
Access the solver currently associated with this system.
Definition: ChSystem.h:124
Irrlicht base classes (Chrono extensions).
const ChQuaterniond QUNIT(1., 0., 0., 0.)
Constant unit quaternion: {1, 0, 0, 0} , corresponds to no rotation (diagonal rotation matrix)
Definition: ChQuaternion.h:431
virtual ChMatrix33 GetGyration() const override
Return the gyration matrix for this solid.
Definition: ChCylinder.cpp:50
Attach a link to the underlying assembly.
Definition: ChSystem.cpp:144
Representation of a 3D transform.
Definition: ChFrame.h:41
ChQuaterniond QuatFromAngleY(double angle)
Convert from a rotation about Y axis to a quaternion.
Definition: ChRotation.cpp:192
Namespace with classes for the Irrlicht module.
Definition: ChApiIrr.h:47
Class for a timer which attempts to enforce soft real-time.
Definition: ChRealtimeStep.h:25
Projected SOR (Successive Over-Relaxation)
void Spin(double step)
Call this function INSIDE the simulation loop, just ONCE per loop (preferably as the last call in the...
Definition: ChRealtimeStep.h:34
void SetSolverType(ChSolver::Type type)
Choose the solver type, to be used for the simultaneous solution of the constraints in dynamical simu...
Definition: ChSystem.cpp:249
int DoStepDynamics(double step_size)
Advance the dynamics simulation by a single time step of given length.
Definition: ChSystem.cpp:1632
ChQuaterniond QuatFromAngleZ(double angle)
Convert from a rotation about Z axis to a quaternion.
Definition: ChRotation.cpp:196
Bullet-based collision detection system.
Representation of a moving 3D.
Definition: ChFrameMoving.h:35
ChVector3< double > ChVector3d
Alias for double-precision vectors.
Definition: ChVector3.h:283
y direction of a reference frame
void ConcatenatePreTransformation(const ChFrameMoving< Real > &F)
Apply a transformation (rotation and translation) represented by another frame.
Definition: ChFrameMoving.h:303
Class defining quaternion objects, that is four-dimensional numbers.
Definition: ChQuaternion.h:34
const ChCoordsys< Real > & GetCoordsys() const
Return both current rotation and translation as a ChCoordsys object.
Definition: ChFrame.h:151
Main Irrlicht namespace (Chrono extensions).
Main namespace for the Chrono package.
Definition: ChCamera.cpp:17
void SetTimestepperType(ChTimestepper::Type type)
Set the method for time integration (time stepper type).
Definition: ChSystem.cpp:412
virtual void SetCollisionSystemType(ChCollisionSystem::Type type)
Set the collision detection system used by this Chrono system to the specified type.
Definition: ChSystem.cpp:324
Irrlicht base classes (Chrono extensions).
Definition: ChIrrTools.cpp:25
Class for a physical system in which contact is modeled using a non-smooth (complementarity-based) me...
Definition: ChSystemNSC.h:29
const ChMatrix33< Real > & GetRotMat() const
Return the current 3x3 rotation matrix.
Definition: ChFrame.h:160