Tutorial covers basic approaches to build systems that embed powertrains made with 1-degree-of-freedom items (rotating shafts):
- connect 1D shafts with transmission ratios
- connect 1D shafts with 1D clutches, brakes, etc.
- connect a 1D shaft to a 3D body.
- No GUI: only text output.
Example 1: create a simple powertrain with ChShaft objects
This models a very basic powertrain with two shafts A
and B
, connected by a reducer [ t ]
with transmission ratio t
. The shafts are free to rotate. Shaft A has an applied torque Ta
, so A and B will constantly accelerate. Each shaft must have some inertia and each one is marked as ||
in the following scheme:
First, create a ChShaft, a 1-degree-of-freedom '1D' mechanical object that can only rotate. It has one inertia value and maybe one applied torque. The ChShaft objects do not have any meaning in 3D, they are just 'building blocks'.
Next, create another shaft. Note that this falls back on the use of shared pointers for ChShaft objects. Also note that the elements must be added to the ChSystem object that manages this simulation.
Finally, create a ChShaftsGear that represents a simplified model of a reducer with transmission ratio t
between two ChShaft objects.
Note that a full blown 3D powertrain could be created using a collection of rigid bodies of ChBody type that would be connected via ChLinkLockRevolute and ChLinkGear 3D constraints. However, this would introduce additional complexity to the model which is not always warranted. The 1D items of ChShaft type keep the model much simpler.
Example 2: a clutch between two shafts
This models a very basic powertrain with two shafts A and B connected by a clutch [ c ]
. Each shaft (see flywheels ||
in schematic below) starts with a nonzero speed and is free to rotate independently until the clutch is activated. At that point, they will decelerate until they have the same speed.
First, create a ChShaft that starts with nonzero angular velocity:
Next, create another ChShaft with opposite initial angular velocity:
Now, create a ChShaftsClutch that represents a simplified model of a clutch between two ChShaft objects (something that limits the max transmitted torque, up to slippage).
Below, the simulation is started with the clutch disengaged:
Example 3: an epicycloidal reducer
An epicycloidal reducer is modeled below using the ChShaftsPlanetary constraint.
The ChShaftsPlanetary sets up a kinematic constraint between three shafts. One of them will be fixed and represents the truss of the reducer - in epicycloidaal reducer, this is the role of the large gear with inner teeth. The two remaining shafts are the input and output shafts. In other cases, such as the differential planetary gear of cars, all three shafts are free. A brake is applied on the output shaft - it is enough that one of these last two shafts is fixed.
In the following schematic, the brake is [ b ]
, the planetary (the reducer) is [ p ]
, the shafts are A,B,C,D
, the applied torque is Ta
, inertias of free shafts are shown as flywheels ||
, and fixed shafts are marked with *
.
A B D
Ta ||---[ p ]---||---[ b ]---*
[ ]---*
C
First, create shaft A with an applied torque:
Next, create shaft B:
Create shaft C, which will be fixed (used as truss of epicycloidal reducer):
Create a ChShaftsPlanetary that represents a simplified model of a planetary gear between three ChShaft objects (e.g., a car differential). An epicycloidal reducer is a special type of planetary gear.
The ratios of the planetary can be set using a simplified formula for the so called Willis case. Imagine we hold fixed the carrier (shaft B in epic. reducers), and leave the truss C free (the outer gear with inner teeth in our reducer); which is the transmission ratio \( t_0 \) that we get. It is simply \( t_0=-Z_a/Z_c \), with \( Z_i \) = num of teeth of gears. Use the following to set all the three ratios:
Now, let's make a shaft D that is fixed and thus used for the right side of a clutch (so the clutch will act as a brake).
Next, make the brake. It is, in fact, a clutch between shafts B and D, where D is fixed as a truss so that the clutch will operate as a brake.
Example 4: constraint between a ChBody and a ChShaft
Suppose we want to create a 3D model, for instance a slider-crank, built with multiple ChBody objects. Moreover, we want to create a powertrain, for instance a motor, a clutch, etc., for the rotation of the crank. Connecting 1D items of ChShaft class with the 3D items of ChBody class calls for the use of a ChShaftsBody constraint, shown as [ bs ]
in the schematic in which the 3D body is shown as <>
. This example also considers a 'torsional spring damper' C
, shown as [ t ]
, that connects shafts A
and C
(C is shown as *
because fixed).
B A C
Ta <>---[ bs ]---||---[ t ]---*
First, create 'A', a 1D shaft:
Next, create 'C', a 1D shaft, fixed:
Create 'B', a 3D rigid body:
Define the torsional spring-damper between shafts A and C:
Define the shaft 'A' connected to the rotation of the 3D body 'B'. Finally, specify the direction (in body coordinates) along which the shaft will affect the body:
Example 5: torque converter and thermal engine
This example highlights the use of a torque converter. The torque converter is represented by a ChShaftsTorqueConverter object that connects three 1D items of ChShaft class:
- the input shaft A, i.e., the impeller connected to the engine
- the output shaft B, i.e., the turbine connected to the gears and wheels
- the stator C, which does not rotate and transmits reaction to the truss In the following schematic, the torque converter is represented as
[ tc ]
, the thermal engine is marked with [ e ]
, and the breaking torque is Tb
(C shown as *
since fixed).
First, create 'A', a 1D shaft:
Next, create 'B', a 1D shaft:
Create 'C', a 1D shaft, fixed:
Create 'D', a 1D shaft, fixed:
Define the torque converter and connect the shafts: A (input), B (output), C(truss stator)
Define the thermal engine acting on shaft A, the input to the torque converter. Note that the thermal engine also requires shaft D, which is used to transmit the reaction torque back to a truss (the motor block).
Option A: use a ChShaftsMotor in MOT_MODE_TORQUE mode. It works, but most often this is more useful when in MOT_MODE_SPEED.
Option B: use a ChShaftsTorque - it simply applies a torque to my_shaftA (say, the crankshaft), and the negative torque to my_shaftD (say, the motor block). It is a quick approach. Note that the torque should be changed at each timestep if a torque curve is to be emulated.
Option C: a more versatile approach where one can define a torque curve and a throttle value via the ChShaftsThermalEngine.
The complete listing:
#include "chrono/physics/ChSystemNSC.h"
#include "chrono/physics/ChShaftsGear.h"
#include "chrono/physics/ChShaftsClutch.h"
#include "chrono/physics/ChShaftsPlanetary.h"
#include "chrono/physics/ChShaftsBody.h"
#include "chrono/physics/ChShaftsTorsionSpring.h"
#include "chrono/physics/ChShaftsTorqueConverter.h"
#include "chrono/physics/ChShaftsMotor.h"
#include "chrono/physics/ChShaftsTorque.h"
#include "chrono/physics/ChShaftsThermalEngine.h"
#include "chrono/physics/ChShaftsFreewheel.h"
#include "chrono/physics/ChShaftsMotorAngle.h"
int main(int argc, char* argv[]) {
GetLog() <<
"Copyright (c) 2017 projectchrono.org\nChrono version: " << CHRONO_VERSION <<
"\n\n";
if (false) {
GetLog() <<
" Example: create a simple power train with ChShaft objects: \n";
auto my_shaftA = chrono_types::make_shared<ChShaft>();
my_shaftA->SetInertia(10);
my_shaftA->SetAppliedTorque(6);
auto my_shaftB = chrono_types::make_shared<ChShaft>();
my_shaftB->SetInertia(100);
my_shaftB->SetShaftFixed(false);
auto my_shaft_gearAB = chrono_types::make_shared<ChShaftsGear>();
my_shaft_gearAB->Initialize(my_shaftA, my_shaftB);
my_shaft_gearAB->SetTransmissionRatio(-0.1);
sys.
Add(my_shaft_gearAB);
GetLog() <<
"\n\n\nHere's the system hierarchy: \n\n ";
double chronoTime = 0;
while (chronoTime < 2.5) {
chronoTime += 0.01;
GetLog() <<
"Time: " << chronoTime <<
"\n"
<< " shaft A rot: " << my_shaftA->GetPos() << " speed: " << my_shaftA->GetPos_dt()
<< " accel: " << my_shaftA->GetPos_dtdt() << "\n"
<< " shaft B rot: " << my_shaftB->GetPos() << " speed: " << my_shaftB->GetPos_dt()
<< " accel: " << my_shaftB->GetPos_dtdt() << "\n"
<< " AB gear, torque on A side: " << my_shaft_gearAB->GetTorqueReactionOn1()
<< " AB gear, torque on B side: " << my_shaft_gearAB->GetTorqueReactionOn2() << "\n";
}
}
if (false) {
GetLog() <<
" Example: a clutch between two shafts \n";
auto my_shaftA = chrono_types::make_shared<ChShaft>();
my_shaftA->SetInertia(0.5);
my_shaftA->SetPos_dt(30);
auto my_shaftB = chrono_types::make_shared<ChShaft>();
my_shaftB->SetInertia(0.6);
my_shaftB->SetPos_dt(-10);
auto my_shaft_clutchAB = chrono_types::make_shared<ChShaftsClutch>();
my_shaft_clutchAB->Initialize(my_shaftA, my_shaftB);
my_shaft_clutchAB->SetTorqueLimit(60);
sys.
Add(my_shaft_clutchAB);
my_shaft_clutchAB->SetModulation(0);
GetLog() <<
"\n\n\nHere's the system hierarchy: \n\n ";
double chronoTime = 0;
while (chronoTime < 1.5) {
chronoTime += 0.01;
if (chronoTime > 0.8) {
my_shaft_clutchAB->SetModulation(1);
}
GetLog() <<
"Time: " << chronoTime <<
"\n"
<< " shaft A rot: " << my_shaftA->GetPos() << " speed: " << my_shaftA->GetPos_dt()
<< " accel: " << my_shaftA->GetPos_dtdt() << "\n"
<< " shaft B rot: " << my_shaftB->GetPos() << " speed: " << my_shaftB->GetPos_dt()
<< " accel: " << my_shaftB->GetPos_dtdt() << "\n"
<< " AB clutch, torque on A side: " << my_shaft_clutchAB->GetTorqueReactionOn1()
<< " AB clutch, torque on B side: " << my_shaft_clutchAB->GetTorqueReactionOn2() << "\n";
}
}
if (false) {
GetLog() <<
" Example: an epicycloidal reducer \n";
auto my_shaftA = chrono_types::make_shared<ChShaft>();
my_shaftA->SetInertia(0.5);
my_shaftA->SetAppliedTorque(10);
auto my_shaftB = chrono_types::make_shared<ChShaft>();
my_shaftB->SetInertia(0.5);
auto my_shaftC = chrono_types::make_shared<ChShaft>();
my_shaftC->SetShaftFixed(true);
auto my_shaft_planetaryBAC = chrono_types::make_shared<ChShaftsPlanetary>();
my_shaft_planetaryBAC->Initialize(my_shaftB, my_shaftA, my_shaftC);
double t0 =
-50.0 / 100.0;
my_shaft_planetaryBAC->SetTransmissionRatioOrdinary(t0);
sys.
Add(my_shaft_planetaryBAC);
auto my_shaftD = chrono_types::make_shared<ChShaft>();
my_shaftD->SetShaftFixed(true);
auto my_shaft_clutchBD = chrono_types::make_shared<ChShaftsClutch>();
my_shaft_clutchBD->Initialize(my_shaftB, my_shaftD);
my_shaft_clutchBD->SetTorqueLimit(60);
sys.
Add(my_shaft_clutchBD);
GetLog() <<
"\n\n\nHere's the system hierarchy: \n\n ";
double chronoTime = 0;
while (chronoTime < 1.5) {
chronoTime += 0.01;
GetLog() <<
"Time: " << chronoTime <<
"\n"
<< " shaft A rot: " << my_shaftA->GetPos() << " speed: " << my_shaftA->GetPos_dt()
<< " accel: " << my_shaftA->GetPos_dtdt() << "\n"
<< " shaft B rot: " << my_shaftB->GetPos() << " speed: " << my_shaftB->GetPos_dt()
<< " accel: " << my_shaftB->GetPos_dtdt() << "\n"
<< " epicycloidal react torques on shafts - on A: "
<< my_shaft_planetaryBAC->GetTorqueReactionOn2()
<< " , on B: " << my_shaft_planetaryBAC->GetTorqueReactionOn1()
<< " , on C: " << my_shaft_planetaryBAC->GetTorqueReactionOn3() << "\n";
}
}
if (false) {
GetLog() <<
" Example: constraint between a ChBody and a ChShaft \n";
auto my_shaftA = chrono_types::make_shared<ChShaft>();
my_shaftA->SetInertia(9);
auto my_shaftC = chrono_types::make_shared<ChShaft>();
my_shaftC->SetShaftFixed(true);
auto my_bodyB = chrono_types::make_shared<ChBody>();
my_bodyB->Accumulate_torque(
ChVector<>(0, 0, 3),
true);
auto my_shaft_torsionAC = chrono_types::make_shared<ChShaftsTorsionSpring>();
my_shaft_torsionAC->Initialize(my_shaftA, my_shaftC);
my_shaft_torsionAC->SetTorsionalStiffness(40);
my_shaft_torsionAC->SetTorsionalDamping(0);
sys.
Add(my_shaft_torsionAC);
auto my_shaftbody_connection = chrono_types::make_shared<ChShaftsBody>();
my_shaftbody_connection->Initialize(my_shaftA, my_bodyB, mshaftdir);
sys.
Add(my_shaftbody_connection);
GetLog() <<
"\n\n\nHere's the system hierarchy: \n\n ";
double chronoTime = 0;
while (chronoTime < 0.5) {
chronoTime += 0.01;
GetLog() <<
"Time: " << chronoTime <<
"\n"
<< " shaft A rot: " << my_shaftA->GetPos() << " speed: " << my_shaftA->GetPos_dt()
<< " accel: " << my_shaftA->GetPos_dtdt() << "\n"
<< " Body B angular speed on z: " << my_bodyB->GetWvel_loc().z()
<< " accel on z: " << my_bodyB->GetWacc_loc().z() << "\n"
<< " AC spring, torque on A side: " << my_shaft_torsionAC->GetTorqueReactionOn1()
<< " torque on C side: " << my_shaft_torsionAC->GetTorqueReactionOn2() << "\n"
<< " shafts/body reaction, on shaft A: " << my_shaftbody_connection->GetTorqueReactionOnShaft()
<< " , on body (x y z): " << my_shaftbody_connection->GetTorqueReactionOnBody().x() << " "
<< my_shaftbody_connection->GetTorqueReactionOnBody().y() << " "
<< my_shaftbody_connection->GetTorqueReactionOnBody().z() << " "
<< "\n";
}
}
if (true) {
GetLog() <<
" Example 5: torque converter and thermal engine \n";
auto my_shaftA = chrono_types::make_shared<ChShaft>();
my_shaftA->SetInertia(1.5);
auto my_shaftB = chrono_types::make_shared<ChShaft>();
my_shaftB->SetInertia(3.2);
my_shaftB->SetAppliedTorque(-5);
auto my_shaftC = chrono_types::make_shared<ChShaft>();
my_shaftC->SetShaftFixed(true);
auto my_shaftD = chrono_types::make_shared<ChShaft>();
my_shaftD->SetShaftFixed(true);
auto my_torqueconverter = chrono_types::make_shared<ChShaftsTorqueConverter>();
my_torqueconverter->Initialize(my_shaftA, my_shaftB, my_shaftC);
sys.
Add(my_torqueconverter);
auto mK = chrono_types::make_shared<ChFunction_Recorder>();
mK->AddPoint(0.0, 15);
mK->AddPoint(0.25, 15);
mK->AddPoint(0.50, 15);
mK->AddPoint(0.75, 16);
mK->AddPoint(0.90, 18);
mK->AddPoint(1.00, 35);
my_torqueconverter->SetCurveCapacityFactor(mK);
auto mT = chrono_types::make_shared<ChFunction_Recorder>();
mT->AddPoint(0.0, 2.00);
mT->AddPoint(0.25, 1.80);
mT->AddPoint(0.50, 1.50);
mT->AddPoint(0.75, 1.15);
mT->AddPoint(1.00, 1.00);
my_torqueconverter->SetCurveTorqueRatio(mT);
auto my_motor = chrono_types::make_shared<ChShaftsThermalEngine>();
my_motor->Initialize(my_shaftA, my_shaftD);
auto mTw = chrono_types::make_shared<ChFunction_Recorder>();
mTw->AddPoint(-5, 30);
mTw->AddPoint(0, 30);
mTw->AddPoint(200, 60);
mTw->AddPoint(400, 40);
mTw->AddPoint(450, 0);
mTw->AddPoint(500, -60);
my_motor->SetTorqueCurve(mTw);
GetLog() <<
"\n\n\nHere's the system hierarchy: \n\n ";
double chronoTime = 0;
while (chronoTime < 4.5) {
chronoTime += 0.01;
GetLog() <<
"Time: " << chronoTime <<
"\n"
<< " shaft A rot: " << my_shaftA->GetPos() << " speed: " << my_shaftA->GetPos_dt()
<< " accel: " << my_shaftA->GetPos_dtdt() << "\n"
<< " shaft B rot: " << my_shaftB->GetPos() << " speed: " << my_shaftB->GetPos_dt()
<< " accel: " << my_shaftB->GetPos_dtdt() << "\n"
<< " T.Convert.:"
<< " R=" << my_torqueconverter->GetSpeedRatio()
<< " Tin=" << my_torqueconverter->GetTorqueReactionOnInput()
<< " Tout=" << my_torqueconverter->GetTorqueReactionOnOutput()
<< " Tstator=" << my_torqueconverter->GetTorqueReactionOnStator() << "\n"
<< " T.Motor: "
<< " T(w)=" << my_motor->GetTorqueReactionOn1() << "[Nm]"
<< " w=" << my_motor->GetRelativeRotation_dt() << "[rad/s]"
<< "\n";
}
}
if (true) {
GetLog() <<
" Example 6: a ratcheting freewheel, as a one-directional clutch \n";
auto my_shaftA = chrono_types::make_shared<ChShaft>();
my_shaftA->SetShaftFixed(true);
auto my_shaftB = chrono_types::make_shared<ChShaft>();
my_shaftB->SetInertia(1.5);
auto my_shaftC = chrono_types::make_shared<ChShaft>();
my_shaftC->SetInertia(3.2);
auto my_shaftD = chrono_types::make_shared<ChShaft>();
my_shaftD->SetShaftFixed(true);
auto my_motor = chrono_types::make_shared<ChShaftsMotorAngle>();
my_motor->Initialize(my_shaftA, my_shaftB);
auto my_sinefunction = chrono_types::make_shared<ChFunction_Sine>(0, 1.2, 0.001+0.5*CH_C_2PI / 20);
my_motor->SetAngleFunction(my_sinefunction);
auto my_freewheel = chrono_types::make_shared<ChShaftsFreewheel>();
my_freewheel->Initialize(my_shaftB, my_shaftC);
my_freewheel->SetRatchetingModeTeeth(25);
auto my_clutch = chrono_types::make_shared<ChShaftsClutch>();
my_clutch->Initialize(my_shaftC, my_shaftD);
my_clutch->SetTorqueLimit(100);
GetLog() <<
"\n\n\nHere's the system hierarchy: \n\n ";
double chronoTime = 0;
double step = 0.01;
while (chronoTime < 5.5) {
chronoTime += step;
GetLog() <<
"Time: " << chronoTime <<
"\n"
<< " shaft B rot: " << my_shaftB->GetPos() << " speed: " << my_shaftB->GetPos_dt()
<< " accel: " << my_shaftB->GetPos_dtdt() << "\n"
<< " shaft C rot: " << my_shaftC->GetPos() << " speed: " << my_shaftC->GetPos_dt()
<< " accel: " << my_shaftC->GetPos_dtdt() << "\n"
<< " Torque: Tmotor=" << my_motor->GetTorqueReactionOn1()
<< " Tfreewheel=" << my_freewheel->GetTorqueReactionOn1()
<< " Tclutch=" << my_clutch->GetTorqueReactionOn1()
<< " ratchet vane=" << my_freewheel->GetCurrentTeethVane()
<< "\n";
file_results << chronoTime << ", "
<< my_shaftB->GetPos() << ", "
<< my_shaftC->GetPos() << ", "
<< my_shaftC->GetPos_dt() << ", "
<< my_clutch->GetTorqueReactionOn1() << ", "
<< my_freewheel->GetCurrentTeethVane() << "\n";
}
}
return 0;
}