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