1 // =============================================================================
2 // PROJECT CHRONO - http://projectchrono.org
3 //
4 // Copyright (c) 2014 projectchrono.org
5 // All rights reserved.
6 //
7 // Use of this source code is governed by a BSD-style license that can be found
8 // in the LICENSE file at the top level of the distribution and at
9 // http://projectchrono.org/license-chrono.txt.
10 //
11 // =============================================================================
12 // Authors: Radu Serban
13 // =============================================================================
14 //
15 // Base class for a rigid suspension with a pinned axle. The rigid axle is pinned
16 // (with a revolute joint) to the chassis, while the spindle bodies are directly
17 // attached to the axle body through revolute joints.
18 //
19 // This is a dependent, non-steerable suspension template.
20 //
21 // The suspension subsystem is modeled with respect to a right-handed frame,
22 // with X pointing towards the front, Y to the left, and Z up (ISO standard).
23 // The suspension reference frame is assumed to be always aligned with that of
24 // the vehicle. When attached to a chassis, only an offset is provided.
25 //
26 // All point locations are assumed to be given for the left half of the
27 // suspension and will be mirrored (reflecting the y coordinates) to construct
28 // the right side.
29 //
30 // =============================================================================
31
32 #include "chrono/assets/ChCylinderShape.h"
33 #include "chrono/assets/ChPointPointDrawing.h"
34 #include "chrono/assets/ChColorAsset.h"
35
36 #include "chrono_vehicle/wheeled_vehicle/suspension/ChRigidPinnedAxle.h"
37
38 namespace chrono {
39 namespace vehicle {
40
41 // -----------------------------------------------------------------------------
42 // -----------------------------------------------------------------------------
ChRigidPinnedAxle(const std::string & name)43 ChRigidPinnedAxle::ChRigidPinnedAxle(const std::string& name) : ChSuspension(name) {}
44
~ChRigidPinnedAxle()45 ChRigidPinnedAxle::~ChRigidPinnedAxle() {
46 auto sys = m_axleTube->GetSystem();
47 if (sys) {
48 sys->Remove(m_axleTube);
49 sys->Remove(m_axlePin);
50 }
51 }
52
53 // -----------------------------------------------------------------------------
54 // -----------------------------------------------------------------------------
Initialize(std::shared_ptr<ChChassis> chassis,std::shared_ptr<ChSubchassis> subchassis,std::shared_ptr<ChSteering> steering,const ChVector<> & location,double left_ang_vel,double right_ang_vel)55 void ChRigidPinnedAxle::Initialize(std::shared_ptr<ChChassis> chassis,
56 std::shared_ptr<ChSubchassis> subchassis,
57 std::shared_ptr<ChSteering> steering,
58 const ChVector<>& location,
59 double left_ang_vel,
60 double right_ang_vel) {
61 m_location = location;
62
63 // Express the suspension reference frame in the absolute coordinate system.
64 ChFrame<> suspension_to_abs(location);
65 suspension_to_abs.ConcatenatePreTransformation(chassis->GetBody()->GetFrame_REF_to_abs());
66
67 // Transform all hardpoints to absolute frame.
68 m_pointsL.resize(NUM_POINTS);
69 m_pointsR.resize(NUM_POINTS);
70 for (int i = 0; i < NUM_POINTS; i++) {
71 ChVector<> rel_pos = getLocation(static_cast<PointId>(i));
72 m_pointsL[i] = suspension_to_abs.TransformLocalToParent(rel_pos);
73 rel_pos.y() = -rel_pos.y();
74 m_pointsR[i] = suspension_to_abs.TransformLocalToParent(rel_pos);
75 }
76
77 // Create the rigid axle and its connection to the chassis
78 ChVector<> axleCOM = suspension_to_abs.TransformLocalToParent(getAxleTubeCOM());
79 m_axleTube = std::shared_ptr<ChBody>(chassis->GetBody()->GetSystem()->NewBody());
80 m_axleTube->SetNameString(m_name + "_axleTube");
81 m_axleTube->SetPos(axleCOM);
82 m_axleTube->SetRot(chassis->GetBody()->GetFrame_REF_to_abs().GetRot());
83 m_axleTube->SetMass(getAxleTubeMass());
84 m_axleTube->SetInertiaXX(getAxleTubeInertia());
85 chassis->GetBody()->GetSystem()->AddBody(m_axleTube);
86
87 m_axlePinLoc = suspension_to_abs.TransformLocalToParent(getAxlePinLocation());
88 ChQuaternion<> chassisRot = chassis->GetBody()->GetFrame_REF_to_abs().GetRot();
89 ChCoordsys<> rev_csys(m_axlePinLoc, chassisRot * Q_from_AngY(CH_C_PI_2));
90 m_axlePin = chrono_types::make_shared<ChLinkLockRevolute>();
91 m_axlePin->SetNameString(m_name + "_axlePin");
92 m_axlePin->Initialize(m_axleTube, chassis->GetBody(), rev_csys);
93 chassis->GetBody()->GetSystem()->AddLink(m_axlePin);
94
95 // Initialize left and right sides.
96 InitializeSide(LEFT, chassis->GetBody(), m_pointsL, left_ang_vel);
97 InitializeSide(RIGHT, chassis->GetBody(), m_pointsR, right_ang_vel);
98 }
99
InitializeSide(VehicleSide side,std::shared_ptr<ChBodyAuxRef> chassis,const std::vector<ChVector<>> & points,double ang_vel)100 void ChRigidPinnedAxle::InitializeSide(VehicleSide side,
101 std::shared_ptr<ChBodyAuxRef> chassis,
102 const std::vector<ChVector<> >& points,
103 double ang_vel) {
104 std::string suffix = (side == LEFT) ? "_L" : "_R";
105
106 // Chassis orientation (expressed in absolute frame)
107 // Recall that the suspension reference frame is aligned with the chassis.
108 ChQuaternion<> chassisRot = chassis->GetFrame_REF_to_abs().GetRot();
109
110 // Create and initialize spindle body (same orientation as the chassis)
111 m_spindle[side] = std::shared_ptr<ChBody>(chassis->GetSystem()->NewBody());
112 m_spindle[side]->SetNameString(m_name + "_spindle" + suffix);
113 m_spindle[side]->SetPos(points[SPINDLE]);
114 m_spindle[side]->SetRot(chassisRot);
115 m_spindle[side]->SetWvel_loc(ChVector<>(0, ang_vel, 0));
116 m_spindle[side]->SetMass(getSpindleMass());
117 m_spindle[side]->SetInertiaXX(getSpindleInertia());
118 chassis->GetSystem()->AddBody(m_spindle[side]);
119
120 // Create and initialize joints
121 ChCoordsys<> rev_csys(points[SPINDLE], chassisRot * Q_from_AngX(CH_C_PI_2));
122 m_revolute[side] = chrono_types::make_shared<ChLinkLockRevolute>();
123 m_revolute[side]->SetNameString(m_name + "_revolute" + suffix);
124 m_revolute[side]->Initialize(m_axleTube, m_spindle[side], rev_csys);
125 chassis->GetSystem()->AddLink(m_revolute[side]);
126
127 // Create and initialize the axle shaft and its connection to the spindle.
128 // Note that the spindle rotates about the Y axis.
129 m_axle[side] = chrono_types::make_shared<ChShaft>();
130 m_axle[side]->SetNameString(m_name + "_axle" + suffix);
131 m_axle[side]->SetInertia(getAxleInertia());
132 m_axle[side]->SetPos_dt(-ang_vel);
133 chassis->GetSystem()->Add(m_axle[side]);
134
135 m_axle_to_spindle[side] = chrono_types::make_shared<ChShaftsBody>();
136 m_axle_to_spindle[side]->SetNameString(m_name + "_axle_to_spindle" + suffix);
137 m_axle_to_spindle[side]->Initialize(m_axle[side], m_spindle[side], ChVector<>(0, -1, 0));
138 chassis->GetSystem()->Add(m_axle_to_spindle[side]);
139 }
140
141 // -----------------------------------------------------------------------------
142 // Get the total mass of the suspension subsystem.
143 // -----------------------------------------------------------------------------
GetMass() const144 double ChRigidPinnedAxle::GetMass() const {
145 return getAxleTubeMass() + 2 * getSpindleMass();
146 }
147
148 // -----------------------------------------------------------------------------
149 // Get the current COM location of the suspension subsystem.
150 // -----------------------------------------------------------------------------
GetCOMPos() const151 ChVector<> ChRigidPinnedAxle::GetCOMPos() const {
152 ChVector<> com(0, 0, 0);
153
154 com += getSpindleMass() * m_spindle[LEFT]->GetPos();
155 com += getSpindleMass() * m_spindle[RIGHT]->GetPos();
156
157 com += getAxleTubeMass() * m_axleTube->GetPos();
158
159 return com / GetMass();
160 }
161
162 // -----------------------------------------------------------------------------
163 // Get the wheel track using the spindle local position.
164 // -----------------------------------------------------------------------------
GetTrack()165 double ChRigidPinnedAxle::GetTrack() {
166 return 2 * getLocation(SPINDLE).y();
167 }
168
169 // -----------------------------------------------------------------------------
170 // Return current suspension forces
171 // -----------------------------------------------------------------------------
ReportSuspensionForce(VehicleSide side) const172 ChSuspension::Force ChRigidPinnedAxle::ReportSuspensionForce(VehicleSide side) const {
173 ChSuspension::Force force{0, 0, 0, 0, 0, 0};
174 return force;
175 }
176
177 // -----------------------------------------------------------------------------
178 // -----------------------------------------------------------------------------
LogConstraintViolations(VehicleSide side)179 void ChRigidPinnedAxle::LogConstraintViolations(VehicleSide side) {
180 // Revolute joint
181 {
182 ChVectorDynamic<> C = m_revolute[side]->GetConstraintViolation();
183 GetLog() << "Spindle revolute ";
184 GetLog() << " " << C(0) << " ";
185 GetLog() << " " << C(1) << " ";
186 GetLog() << " " << C(2) << " ";
187 GetLog() << " " << C(3) << " ";
188 GetLog() << " " << C(4) << "\n";
189 }
190 }
191
192 // -----------------------------------------------------------------------------
193 // -----------------------------------------------------------------------------
AddVisualizationAssets(VisualizationType vis)194 void ChRigidPinnedAxle::AddVisualizationAssets(VisualizationType vis) {
195 ChSuspension::AddVisualizationAssets(vis);
196
197 if (vis == VisualizationType::NONE)
198 return;
199
200 // Express points in local frame of the axle tube
201 ChVector<> pSL = m_axleTube->TransformPointParentToLocal(m_pointsL[SPINDLE]);
202 ChVector<> pSR = m_axleTube->TransformPointParentToLocal(m_pointsR[SPINDLE]);
203 ChVector<> pP = m_axleTube->TransformPointParentToLocal(m_axlePinLoc);
204
205 // Add visualization assets for the axle tube body
206 auto cyl1 = chrono_types::make_shared<ChCylinderShape>();
207 cyl1->GetCylinderGeometry().p1 = pSL;
208 cyl1->GetCylinderGeometry().p2 = pSR;
209 cyl1->GetCylinderGeometry().rad = getAxleTubeRadius();
210 m_axleTube->AddAsset(cyl1);
211
212 static const double threshold2 = 1e-6;
213 if (pP.Length2() > threshold2) {
214 auto cyl2 = chrono_types::make_shared<ChCylinderShape>();
215 cyl2->GetCylinderGeometry().p1 = pP;
216 cyl2->GetCylinderGeometry().p2 = ChVector<>(0, 0, 0);
217 cyl2->GetCylinderGeometry().rad = getAxleTubeRadius() / 2;
218 m_axleTube->AddAsset(cyl2);
219 }
220
221 auto cyl3 = chrono_types::make_shared<ChCylinderShape>();
222 cyl3->GetCylinderGeometry().p1 = pP - ChVector<>(1.5 * getAxleTubeRadius(), 0, 0);
223 cyl3->GetCylinderGeometry().p2 = pP + ChVector<>(1.5 * getAxleTubeRadius(), 0, 0);
224 cyl3->GetCylinderGeometry().rad = getAxleTubeRadius();
225 m_axleTube->AddAsset(cyl3);
226
227 auto col = chrono_types::make_shared<ChColorAsset>(0.2f, 0.2f, 0.6f);
228 m_axleTube->AddAsset(col);
229 }
230
RemoveVisualizationAssets()231 void ChRigidPinnedAxle::RemoveVisualizationAssets() {
232 ChSuspension::RemoveVisualizationAssets();
233
234 m_axleTube->GetAssets().clear();
235 }
236 // -----------------------------------------------------------------------------
237 // -----------------------------------------------------------------------------
ExportComponentList(rapidjson::Document & jsonDocument) const238 void ChRigidPinnedAxle::ExportComponentList(rapidjson::Document& jsonDocument) const {
239 ChPart::ExportComponentList(jsonDocument);
240
241 std::vector<std::shared_ptr<ChBody>> bodies;
242 bodies.push_back(m_spindle[0]);
243 bodies.push_back(m_spindle[1]);
244 bodies.push_back(m_axleTube);
245 ChPart::ExportBodyList(jsonDocument, bodies);
246
247 std::vector<std::shared_ptr<ChShaft>> shafts;
248 shafts.push_back(m_axle[0]);
249 shafts.push_back(m_axle[1]);
250 ChPart::ExportShaftList(jsonDocument, shafts);
251
252 std::vector<std::shared_ptr<ChLink>> joints;
253 joints.push_back(m_revolute[0]);
254 joints.push_back(m_revolute[1]);
255 joints.push_back(m_axlePin);
256 ChPart::ExportJointList(jsonDocument, joints);
257 }
258
Output(ChVehicleOutput & database) const259 void ChRigidPinnedAxle::Output(ChVehicleOutput& database) const {
260 if (!m_output)
261 return;
262
263 std::vector<std::shared_ptr<ChBody>> bodies;
264 bodies.push_back(m_spindle[0]);
265 bodies.push_back(m_spindle[1]);
266 bodies.push_back(m_axleTube);
267 database.WriteBodies(bodies);
268
269 std::vector<std::shared_ptr<ChShaft>> shafts;
270 shafts.push_back(m_axle[0]);
271 shafts.push_back(m_axle[1]);
272 database.WriteShafts(shafts);
273
274 std::vector<std::shared_ptr<ChLink>> joints;
275 joints.push_back(m_revolute[0]);
276 joints.push_back(m_revolute[1]);
277 joints.push_back(m_axlePin);
278 database.WriteJoints(joints);
279 }
280
281 } // end namespace vehicle
282 } // end namespace chrono
283