CARLA
ChronoMovementComponent.cpp
Go to the documentation of this file.
1 // Copyright (c) 2021 Computer Vision Center (CVC) at the Universitat Autonoma
2 // de Barcelona (UAB).
3 // Copyright (c) 2019 Intel Corporation
4 //
5 // This work is licensed under the terms of the MIT license.
6 // For a copy, see <https://opensource.org/licenses/MIT>.
7 
11 
13 #include <carla/rpc/String.h>
14 #ifdef WITH_CHRONO
15 #include "chrono_vehicle/utils/ChUtilsJSON.h"
16 #endif
18 #include "Carla/Util/RayTracer.h"
19 
20 
23  uint64_t MaxSubsteps,
24  float MaxSubstepDeltaTime,
25  FString VehicleJSON,
26  FString PowertrainJSON,
27  FString TireJSON,
28  FString BaseJSONPath)
29 {
30  #ifdef WITH_CHRONO
31  UChronoMovementComponent* ChronoMovementComponent = NewObject<UChronoMovementComponent>(Vehicle);
32  if (!VehicleJSON.IsEmpty())
33  {
34  ChronoMovementComponent->VehicleJSON = VehicleJSON;
35  }
36  if (!PowertrainJSON.IsEmpty())
37  {
38  ChronoMovementComponent->PowertrainJSON = PowertrainJSON;
39  }
40  if (!TireJSON.IsEmpty())
41  {
42  ChronoMovementComponent->TireJSON = TireJSON;
43  }
44  if (!BaseJSONPath.IsEmpty())
45  {
46  ChronoMovementComponent->BaseJSONPath = BaseJSONPath;
47  }
48  ChronoMovementComponent->MaxSubsteps = MaxSubsteps;
49  ChronoMovementComponent->MaxSubstepDeltaTime = MaxSubstepDeltaTime;
50  Vehicle->SetCarlaMovementComponent(ChronoMovementComponent);
51  ChronoMovementComponent->RegisterComponent();
52  #else
53  UE_LOG(LogCarla, Warning, TEXT("Error: Chrono is not enabled") );
54  #endif
55 }
56 
57 #ifdef WITH_CHRONO
58 
59 using namespace chrono;
60 using namespace chrono::vehicle;
61 
62 constexpr double CMTOM = 0.01;
63 ChVector<> UE4LocationToChrono(const FVector& Location)
64 {
65  return CMTOM*ChVector<>(Location.X, -Location.Y, Location.Z);
66 }
67 constexpr double MTOCM = 100;
68 FVector ChronoToUE4Location(const ChVector<>& position)
69 {
70  return MTOCM*FVector(position.x(), -position.y(), position.z());
71 }
72 ChVector<> UE4DirectionToChrono(const FVector& Location)
73 {
74  return ChVector<>(Location.X, -Location.Y, Location.Z);
75 }
76 FVector ChronoToUE4Direction(const ChVector<>& position)
77 {
78  return FVector(position.x(), -position.y(), position.z());
79 }
80 ChQuaternion<> UE4QuatToChrono(const FQuat& Quat)
81 {
82  return ChQuaternion<>(Quat.W, -Quat.X, Quat.Y, -Quat.Z);
83 }
84 FQuat ChronoToUE4Quat(const ChQuaternion<>& quat)
85 {
86  return FQuat(-quat.e1(), quat.e2(), -quat.e3(), quat.e0());
87 }
88 
89 UERayCastTerrain::UERayCastTerrain(
90  ACarlaWheeledVehicle* UEVehicle,
91  chrono::vehicle::ChVehicle* ChrVehicle)
92  : CarlaVehicle(UEVehicle), ChronoVehicle(ChrVehicle) {}
93 
94 std::pair<bool, FHitResult>
95  UERayCastTerrain::GetTerrainProperties(const FVector &Location) const
96 {
97  const double MaxDistance = 1000000;
98  FVector StartLocation = Location;
99  FVector EndLocation = Location + FVector(0,0,-1)*MaxDistance; // search downwards
100  FHitResult Hit;
101  FCollisionQueryParams CollisionQueryParams;
102  CollisionQueryParams.AddIgnoredActor(CarlaVehicle);
103  bool bDidHit = CarlaVehicle->GetWorld()->LineTraceSingleByChannel(
104  Hit,
105  StartLocation,
106  EndLocation,
107  ECC_GameTraceChannel2, // camera (any collision)
108  CollisionQueryParams,
109  FCollisionResponseParams()
110  );
111  return std::make_pair(bDidHit, Hit);
112 }
113 
114 double UERayCastTerrain::GetHeight(const ChVector<>& loc) const
115 {
116  FVector Location = ChronoToUE4Location(loc + ChVector<>(0,0,0.5)); // small offset to detect the ground properly
117  auto point_pair = GetTerrainProperties(Location);
118  if (point_pair.first)
119  {
120  double Height = CMTOM*static_cast<double>(point_pair.second.Location.Z);
121  return Height;
122  }
123  return -1000000.0;
124 }
125 ChVector<> UERayCastTerrain::GetNormal(const ChVector<>& loc) const
126 {
127  FVector Location = ChronoToUE4Location(loc);
128  auto point_pair = GetTerrainProperties(Location);
129  if (point_pair.first)
130  {
131  FVector Normal = point_pair.second.Normal;
132  auto ChronoNormal = UE4DirectionToChrono(Normal);
133  return ChronoNormal;
134  }
135  return UE4DirectionToChrono(FVector(0,0,1));
136 }
137 float UERayCastTerrain::GetCoefficientFriction(const ChVector<>& loc) const
138 {
139  return 1;
140 }
141 
143 {
144  Super::BeginPlay();
145 
147 
148  // // // Chrono System
149  Sys.Set_G_acc(ChVector<>(0, 0, -9.81));
150  Sys.SetSolverType(ChSolver::Type::BARZILAIBORWEIN);
151  Sys.SetSolverMaxIterations(150);
152  Sys.SetMaxPenetrationRecoverySpeed(4.0);
153 
154  InitializeChronoVehicle();
155 
156  // Create the terrain
157  Terrain = chrono_types::make_shared<UERayCastTerrain>(CarlaVehicle, Vehicle.get());
158 
159  CarlaVehicle->OnActorHit.AddDynamic(
161  CarlaVehicle->GetMesh()->OnComponentBeginOverlap.AddDynamic(
163  CarlaVehicle->GetMesh()->SetCollisionResponseToChannel(
164  ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Overlap);
165 }
166 
167 void UChronoMovementComponent::InitializeChronoVehicle()
168 {
169  // Initial location with small offset to prevent falling through the ground
170  FVector VehicleLocation = CarlaVehicle->GetActorLocation() + FVector(0,0,25);
171  FQuat VehicleRotation = CarlaVehicle->GetActorRotation().Quaternion();
172  auto ChronoLocation = UE4LocationToChrono(VehicleLocation);
173  auto ChronoRotation = UE4QuatToChrono(VehicleRotation);
174 
175  // Set base path for vehicle JSON files
176  vehicle::SetDataPath(carla::rpc::FromFString(BaseJSONPath));
177 
178  std::string BasePath_string = carla::rpc::FromFString(BaseJSONPath);
179 
180  // Create full path for json files
181  // Do NOT use vehicle::GetDataFile() as strings from chrono lib
182  // messes with unreal's std lib
183  std::string VehicleJSON_string = carla::rpc::FromFString(VehicleJSON);
184  std::string VehiclePath_string = BasePath_string + VehicleJSON_string;
185  FString VehicleJSONPath = carla::rpc::ToFString(VehiclePath_string);
186 
187  std::string PowerTrainJSON_string = carla::rpc::FromFString(PowertrainJSON);
188  std::string PowerTrain_string = BasePath_string + PowerTrainJSON_string;
189  FString PowerTrainJSONPath = carla::rpc::ToFString(PowerTrain_string);
190 
191  std::string TireJSON_string = carla::rpc::FromFString(TireJSON);
192  std::string Tire_string = BasePath_string + TireJSON_string;
193  FString TireJSONPath = carla::rpc::ToFString(Tire_string);
194 
195  UE_LOG(LogCarla, Log, TEXT("Loading Chrono files: Vehicle: %s, PowerTrain: %s, Tire: %s"),
196  *VehicleJSONPath,
197  *PowerTrainJSONPath,
198  *TireJSONPath);
199  // Create JSON vehicle
200  Vehicle = chrono_types::make_shared<WheeledVehicle>(
201  &Sys,
202  VehiclePath_string);
203  Vehicle->Initialize(ChCoordsys<>(ChronoLocation, ChronoRotation));
204  Vehicle->GetChassis()->SetFixed(false);
205  // Create and initialize the powertrain System
206  auto powertrain = ReadPowertrainJSON(
207  PowerTrain_string);
208  Vehicle->InitializePowertrain(powertrain);
209  // Create and initialize the tires
210  for (auto& axle : Vehicle->GetAxles()) {
211  for (auto& wheel : axle->GetWheels()) {
212  auto tire = ReadTireJSON(Tire_string);
213  Vehicle->InitializeTire(tire, wheel, VisualizationType::MESH);
214  }
215  }
216 }
217 
219 {
221  auto PowerTrain = Vehicle->GetPowertrain();
222  if (PowerTrain)
223  {
225  {
226  PowerTrain->SetDriveMode(ChPowertrain::DriveMode::REVERSE);
227  }
228  else
229  {
230  PowerTrain->SetDriveMode(ChPowertrain::DriveMode::FORWARD);
231  }
232  }
233 }
234 
235 void UChronoMovementComponent::TickComponent(float DeltaTime,
236  ELevelTick TickType,
237  FActorComponentTickFunction* ThisTickFunction)
238 {
239  TRACE_CPUPROFILER_EVENT_SCOPE(UChronoMovementComponent::TickComponent);
240  if (DeltaTime > MaxSubstepDeltaTime)
241  {
242  uint64_t NumberSubSteps =
243  FGenericPlatformMath::FloorToInt(DeltaTime/MaxSubstepDeltaTime);
244  if (NumberSubSteps < MaxSubsteps)
245  {
246  for (uint64_t i = 0; i < NumberSubSteps; ++i)
247  {
248  AdvanceChronoSimulation(MaxSubstepDeltaTime);
249  }
250  float RemainingTime = DeltaTime - NumberSubSteps*MaxSubstepDeltaTime;
251  if (RemainingTime > 0)
252  {
253  AdvanceChronoSimulation(RemainingTime);
254  }
255  }
256  else
257  {
258  double SubDelta = DeltaTime / MaxSubsteps;
259  for (uint64_t i = 0; i < MaxSubsteps; ++i)
260  {
261  AdvanceChronoSimulation(SubDelta);
262  }
263  }
264  }
265  else
266  {
267  AdvanceChronoSimulation(DeltaTime);
268  }
269 
270  const auto ChronoPositionOffset = ChVector<>(0,0,-0.25f);
271  auto VehiclePos = Vehicle->GetVehiclePos() + ChronoPositionOffset;
272  auto VehicleRot = Vehicle->GetVehicleRot();
273  double Time = Vehicle->GetSystem()->GetChTime();
274 
275  FVector NewLocation = ChronoToUE4Location(VehiclePos);
276  FQuat NewRotation = ChronoToUE4Quat(VehicleRot);
277  if(NewLocation.ContainsNaN() || NewRotation.ContainsNaN())
278  {
279  UE_LOG(LogCarla, Warning, TEXT(
280  "Error: Chrono vehicle position or rotation contains NaN. Disabling chrono physics..."));
282  return;
283  }
284  CarlaVehicle->SetActorLocation(NewLocation);
285  FRotator NewRotator = NewRotation.Rotator();
286  // adding small rotation to compensate chrono offset
287  const float ChronoPitchOffset = 2.5f;
288  NewRotator.Add(ChronoPitchOffset, 0.f, 0.f);
289  CarlaVehicle->SetActorRotation(NewRotator);
290 }
291 
292 void UChronoMovementComponent::AdvanceChronoSimulation(float StepSize)
293 {
294  double Time = Vehicle->GetSystem()->GetChTime();
295  double Throttle = VehicleControl.Throttle;
296  double Steering = -VehicleControl.Steer; // RHF to LHF
298  Vehicle->Synchronize(Time, {Steering, Throttle, Brake}, *Terrain.get());
299  Vehicle->Advance(StepSize);
300  Sys.DoStepDynamics(StepSize);
301 }
302 
304 {
305  if (Vehicle)
306  {
307  return ChronoToUE4Location(
308  Vehicle->GetVehiclePointVelocity(ChVector<>(0,0,0)));
309  }
310  return FVector();
311 }
312 
314 {
315  if (Vehicle)
316  {
317  auto PowerTrain = Vehicle->GetPowertrain();
318  if (PowerTrain)
319  {
320  return PowerTrain->GetCurrentTransmissionGear();
321  }
322  }
323  return 0;
324 }
325 
327 {
328  if (Vehicle)
329  {
330  return GetVelocity().X;
331  }
332  return 0.f;
333 }
334 
335 void UChronoMovementComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
336 {
337  if(!CarlaVehicle)
338  {
339  return;
340  }
341  // reset callbacks to react to collisions
342  CarlaVehicle->OnActorHit.RemoveDynamic(
344  CarlaVehicle->GetMesh()->OnComponentBeginOverlap.RemoveDynamic(
346  CarlaVehicle->GetMesh()->SetCollisionResponseToChannel(
347  ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block);
348 }
349 #endif
350 
352 {
353  this->SetComponentTickEnabled(false);
355  CarlaVehicle->OnActorHit.RemoveDynamic(this, &UChronoMovementComponent::OnVehicleHit);
356  CarlaVehicle->GetMesh()->OnComponentBeginOverlap.RemoveDynamic(
358  CarlaVehicle->GetMesh()->SetCollisionResponseToChannel(
359  ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block);
361  carla::log_warning("Chrono physics does not support collisions yet, reverting to default PhysX physics.");
362 }
363 
365  AActor *OtherActor,
366  FVector NormalImpulse,
367  const FHitResult &Hit)
368 {
370 }
371 
372 // On car mesh overlap, only works when carsim is enabled
373 // (this event triggers when overlapping with static environment)
375  UPrimitiveComponent* OverlappedComponent,
376  AActor* OtherActor,
377  UPrimitiveComponent* OtherComp,
378  int32 OtherBodyIndex,
379  bool bFromSweep,
380  const FHitResult & SweepResult)
381 {
382  if (OtherComp->GetCollisionResponseToChannel(
383  ECollisionChannel::ECC_WorldDynamic) ==
384  ECollisionResponse::ECR_Block)
385  {
387  }
388 }
virtual void ProcessControl(FVehicleControl &Control)
void OnVehicleHit(AActor *Actor, AActor *OtherActor, FVector NormalImpulse, const FHitResult &Hit)
geom::Location Location
Definition: rpc/Location.h:14
carla::SharedPtr< cc::Actor > Actor
void OnVehicleOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
static void log_warning(Args &&... args)
Definition: Logging.h:96
static void CreateChronoMovementComponent(ACarlaWheeledVehicle *Vehicle, uint64_t MaxSubsteps, float MaxSubstepDeltaTime, FString VehicleJSON="", FString PowertrainJSON="", FString TireJSON="", FString BaseJSONPath="")
virtual float GetVehicleForwardSpeed() const
void EnableUE4VehiclePhysics(bool bResetVelocity=true)
Base class for CARLA wheeled vehicles.
static void CreateDefaultMovementComponent(ACarlaWheeledVehicle *Vehicle)
void SetCarlaMovementComponent(UBaseCarlaMovementComponent *MoementComponent)