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 track assembly using single-pin track shoes
16 // (template definitions).
17 //
18 // The reference frame for a vehicle follows the ISO standard: Z-axis up, X-axis
19 // pointing forward, and Y-axis towards the left of the vehicle.
20 //
21 // =============================================================================
22
23 #include <cmath>
24
25 #include "chrono/core/ChLog.h"
26
27 #include "chrono_vehicle/tracked_vehicle/track_assembly/ChTrackAssemblySinglePin.h"
28
29 namespace chrono {
30 namespace vehicle {
31
ChTrackAssemblySinglePin(const std::string & name,VehicleSide side)32 ChTrackAssemblySinglePin::ChTrackAssemblySinglePin(const std::string& name, VehicleSide side)
33 : ChTrackAssemblySegmented(name, side) {}
34
~ChTrackAssemblySinglePin()35 ChTrackAssemblySinglePin::~ChTrackAssemblySinglePin() {}
36
37 // -----------------------------------------------------------------------------
38 // Assemble track shoes over wheels.
39 //
40 // Returns true if the track shoes were initialized in a counter clockwise
41 // direction and false otherwise.
42 //
43 // This procedure is performed in the chassis reference frame, taking into
44 // account the convention that the chassis reference frame has the x-axis
45 // pointing to the front of the vehicle and the z-axis pointing up.
46 // It is also assumed that the sprocket, idler, and road wheels lie in the
47 // same vertical plane (in the chassis reference frame). The assembly is done
48 // in the (z-x) plane.
49 //
50 // TODO: may need fixes for clock-wise wrapping (idler in front of sprocket)
51 //
52 // -----------------------------------------------------------------------------
Assemble(std::shared_ptr<ChBodyAuxRef> chassis)53 bool ChTrackAssemblySinglePin::Assemble(std::shared_ptr<ChBodyAuxRef> chassis) {
54 // Number of track shoes and road wheels.
55 size_t num_shoes = m_shoes.size();
56 size_t num_wheels = m_suspensions.size();
57 size_t index = 0;
58
59 // Positions of sprocket, idler, and (front and rear) wheels.
60 const ChVector<>& sprocket_pos = chassis->TransformPointParentToLocal(m_sprocket->GetGearBody()->GetPos());
61 const ChVector<>& idler_pos = chassis->TransformPointParentToLocal(m_idler->GetWheelBody()->GetPos());
62
63 ChVector<> front_wheel_pos = chassis->TransformPointParentToLocal(m_suspensions[0]->GetWheelBody()->GetPos());
64 ChVector<> rear_wheel_pos = front_wheel_pos;
65 for (size_t i = 1; i < num_wheels; i++) {
66 const ChVector<>& wheel_pos = chassis->TransformPointParentToLocal(m_suspensions[i]->GetWheelBody()->GetPos());
67 if (wheel_pos.x() > front_wheel_pos.x())
68 front_wheel_pos = wheel_pos;
69 if (wheel_pos.x() < rear_wheel_pos.x())
70 rear_wheel_pos = wheel_pos;
71 }
72
73 // Subsystem parameters.
74 // Note that the idler and wheel radii are inflated by a fraction of the shoe height.
75 double shoe_pitch = m_shoes[0]->GetPitch();
76 double shoe_height = m_shoes[0]->GetHeight();
77 double sprocket_radius = m_sprocket->GetAssemblyRadius();
78 double idler_radius = m_idler->GetWheelRadius() + 0.9 * shoe_height;
79 double wheel_radius = m_suspensions[0]->GetWheelRadius() + 0.9 * shoe_height;
80
81 // Decide whether we wrap counter-clockwise (sprocket in front of idler) or
82 // clockwise (sprocket behind idler).
83 bool ccw = sprocket_pos.x() > idler_pos.x();
84 double sign = ccw ? -1 : +1;
85 const ChVector<>& wheel_sprocket_pos = ccw ? front_wheel_pos : rear_wheel_pos;
86 const ChVector<>& wheel_idler_pos = ccw ? rear_wheel_pos : front_wheel_pos;
87
88 // 1. Create shoes around the sprocket, starting under the sprocket and
89 // moving away from the idler. Stop before creating a horizontal track
90 // shoe above the sprocket.
91
92 // Initialize the location of the first shoe connection point.
93 ChVector<> p0 = sprocket_pos - ChVector<>(0, 0, sprocket_radius);
94 ChVector<> p1 = p0;
95 ChVector<> p2;
96
97 // Calculate the incremental pitch angle around the sprocket.
98 double tmp = shoe_pitch / (2 * sprocket_radius);
99 double delta_angle = sign * std::asin(tmp);
100 double angle = delta_angle;
101
102 // Create track shoes around the sprocket.
103 while (std::abs(angle) < CH_C_PI && index < num_shoes) {
104 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
105 m_shoes[index]->SetIndex(index);
106 m_shoes[index]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
107 p1 = p2;
108 angle += 2 * delta_angle;
109 ++index;
110 }
111
112 // 2. Create shoes between sprocket and idler. These shoes are parallel to a
113 // line connecting the top points of the sprocket gear and idler wheel.
114 // We target a point that lies above the idler by slightly more than the
115 // track shoe's height and stop when we reach the idler location.
116
117 // Calculate the constant pitch angle.
118 double dz = (sprocket_pos.z() + sprocket_radius) - (idler_pos.z() + idler_radius);
119 double dx = sprocket_pos.x() - idler_pos.x();
120 angle = ccw ? -CH_C_PI - std::atan2(dz, dx) : CH_C_PI + std::atan2(dz, -dx);
121
122 // Create track shoes with constant orientation
123 while (sign * (idler_pos.x() - p2.x()) > shoe_pitch && index < num_shoes) {
124 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
125 m_shoes[index]->SetIndex(index);
126 m_shoes[index]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
127 p1 = p2;
128 ++index;
129 }
130
131 // 3. Create shoes around the idler wheel. Stop when we wrap under the idler.
132
133 // Calculate the incremental pitch angle around the idler.
134 tmp = shoe_pitch / (2 * idler_radius);
135 delta_angle = sign * std::asin(tmp);
136
137 while (std::abs(angle) < CH_C_2PI && index < num_shoes) {
138 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
139 m_shoes[index]->SetIndex(index);
140 m_shoes[index]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
141 p1 = p2;
142 angle += 2 * delta_angle;
143 ++index;
144 }
145
146 // 4. Create shoes between idler and closest road wheel. The shoes are parallel
147 // to a line connecting bottom points on idler and wheel. Stop when passing
148 // the wheel position.
149
150 dz = (idler_pos.z() - idler_radius) - (wheel_idler_pos.z() - wheel_radius);
151 dx = idler_pos.x() - wheel_idler_pos.x();
152 angle = ccw ? -CH_C_2PI + std::atan2(dz, -dx) : -CH_C_2PI - std::atan2(dz, dx);
153
154 // Create track shoes with constant orientation
155 while (sign * (p2.x() - wheel_idler_pos.x()) > 0 && index < num_shoes) {
156 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
157 m_shoes[index]->SetIndex(index);
158 m_shoes[index]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
159 p1 = p2;
160 ++index;
161 }
162
163 // 5. Create shoes below the road wheels. These shoes are horizontal. Stop when
164 // passing the position of the wheel closest to the sprocket.
165
166 angle = ccw ? 0 : CH_C_2PI;
167
168 while (sign * (p2.x() - wheel_sprocket_pos.x()) > 0 && index < num_shoes) {
169 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
170 m_shoes[index]->SetIndex(index);
171 m_shoes[index]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
172 p1 = p2;
173 ++index;
174 }
175
176 // 6. If we have an odd number of track shoes left, create one more horizontal shoe.
177
178 size_t num_left = num_shoes - index;
179
180 if (num_left % 2 == 1) {
181 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
182 m_shoes[index]->SetIndex(index);
183 m_shoes[index]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
184 p1 = p2;
185 ++index;
186 --num_left;
187 }
188
189 // 7. Check if the remaining shoes are enough to close the loop.
190
191 double gap = (p0 - p1).Length();
192
193 if (num_left * shoe_pitch < gap) {
194 GetLog() << "\nInsufficient number of track shoes for this configuration.\n";
195 GetLog() << "Missing distance: " << gap - num_left * shoe_pitch << "\n\n";
196 for (size_t i = index; i < num_shoes; i++) {
197 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
198 m_shoes[i]->SetIndex(i);
199 m_shoes[i]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
200 p1 = p2;
201 }
202 return ccw;
203 }
204
205 // 8. Complete the loop using the remaining shoes (always an even number)
206 // Form an isosceles triangle connecting the last initialized shoe with
207 // the very first one under the sprocket.
208
209 double alpha = sign * std::atan2(p0.z() - p1.z(), -sign * (p0.x() - p1.x()));
210 double beta = std::acos(gap / (shoe_pitch * num_left));
211
212 // Create half of the remaining shoes (with a pitch angle = alpha-beta).
213 angle = alpha - sign * beta;
214 for (size_t i = 0; i < num_left / 2; i++) {
215 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
216 m_shoes[index]->SetIndex(index);
217 m_shoes[index]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
218 p1 = p2;
219 ++index;
220 }
221
222 // Create the second half of the remaining shoes (pitch angle = alpha+beta).
223 angle = alpha + sign * beta;
224 for (size_t i = 0; i < num_left / 2; i++) {
225 p2 = p1 - sign * shoe_pitch * ChVector<>(std::cos(angle), 0, -std::sin(angle));
226 m_shoes[index]->SetIndex(index);
227 m_shoes[index]->Initialize(chassis, 0.5 * (p1 + p2), Q_from_AngY(angle));
228 p1 = p2;
229 ++index;
230 }
231
232 ////GetLog() << "Track assembly done. Number of track shoes: " << index << "\n";
233 return ccw;
234 }
235
RemoveTrackShoes()236 void ChTrackAssemblySinglePin::RemoveTrackShoes() {
237 m_shoes.clear();
238 }
239
240 } // end namespace vehicle
241 } // end namespace chrono
242