Agrarsense
PIDDrone.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
12
13#include "ROSIntegration/Classes/RI/Topic.h"
14
15#include "Camera/CameraComponent.h"
16#include "Field/FieldSystemNodes.h"
17#include "Algo/Reverse.h"
18
19#if WITH_EDITOR
20#include "DrawDebugHelpers.h"
21#endif
22
24{
25 PrimaryActorTick.bCanEverTick = true;
26 InteractableName = NSLOCTEXT("Agrarsense", "DroneInteractableName", "Drone");
27}
28
30{
31 Super::BeginPlay();
32
33 World = GetWorld();
34
35 ROSMessage.Reset();
36 ROSMessage = MakeShared<ROSMessages::std_msgs::Float32>();
37
39
40 DroneSkeletalMesh = Cast<USkeletalMeshComponent>(GetComponentByClass(USkeletalMeshComponent::StaticClass()));
41
42 PositionMesh = Cast<UStaticMeshComponent>(GetComponentByClass(UStaticMeshComponent::StaticClass()));
43
44 StartingPosition = GetActorLocation();
45
46
48 {
49 // Create Transform sensor for forwarder and harvester
51 Params.SaveTransformDataToDisk = true;
52 Params.OwningActor = this;
53 Params.UseOwningActorTransform = false;
54 Params.PrimitiveComponent = Cast<UPrimitiveComponent>(DroneSkeletalMesh);
55
56 FString VehicleTransformSensorID = ActorID + "/transform";
57 TransformSensor = USensorFactory::SpawnTransformSensor(GetActorTransform(), Params, VehicleTransformSensorID, "transform", true, this);
58 }
59}
60
61void APIDDrone::EndPlay(const EEndPlayReason::Type EndPlayReason)
62{
63 Super::EndPlay(EndPlayReason);
64
65 ROSMessage.Reset();
67}
68
69void APIDDrone::Tick(float DeltaTime)
70{
71 Super::Tick(DeltaTime);
72
73 if (!IsVehicleInGarage())
74 {
75 AutoPilot(DeltaTime);
77 }
78}
79
80void APIDDrone::AutoPilot(const float DeltaTime)
81{
83 {
84 return;
85 }
86
88}
89
91{
92 // TODO: Check if in garage
93 // include drone rotation to point
94
95 // Don't try to fly if no waypoints
96 if (DroneParameters.Points.Num() == 0 && IsRoaming())
97 {
99 }
100
101 FVector waypoint = GetCurrentWaypointTarget();
102 FVector currentlocation = DroneSkeletalMesh->GetRelativeTransform().GetLocation();
103
104 if (PositionMesh)
105 {
106 // Doesn't work
107 PositionMesh->SetWorldLocation(waypoint);
108 }
109#if WITH_EDITOR
110 else
111 {
112 UE_LOG(LogTemp, Warning, TEXT("Desired location mesh not found"));
113 //DrawDebugLine(World, currentlocation, waypoint, FColor::Red, false, 0.0f, 0.0f, 5.0f);
114 }
115#endif
116
117 if (FVector2f::Distance(FVector2f(waypoint.X, waypoint.Y), FVector2f(currentlocation.X, currentlocation.Y)) < 1000)
118 {
119 waypointReached = true;
120 if (passedWaypoints != DroneParameters.Points.Num() - 1)
121 {
122#if WITH_EDITOR
123 UE_LOG(LogTemp, Warning, TEXT("Changing waypoint from %i to %i"), passedWaypoints, passedWaypoints + 1);
124#endif
125
127 }
128 else
129 {
131 {
133 passedWaypoints = 0;
134 }
135 else
136 {
138 {
141 break;
144 passedWaypoints = 0;
145 break;
147 passedWaypoints = 0;
148 break;
150 Algo::Reverse(DroneParameters.Points);
151 passedWaypoints = 0;
152 break;
153 default:
154 break;
155 }
156 }
157 }
158 }
159}
160
161void APIDDrone::MoveDroneToPosition(const FTransform Transform)
162{
163 if (DroneParameters.Points.Num() > 0)
164 {
165 DroneParameters.Points.Empty();
167 }
168 else
169 {
171 }
172}
173
175{
176 // UE_LOG(LogTemp, Warning, TEXT(" % s"), *DroneParameters.Points[passedWaypoints].GetLocation().ToString());
178 {
179 return FVector(0, 0, 0);
180 }
181
182 return DroneParameters.Points[passedWaypoints].GetLocation();
183}
184
185void APIDDrone::AssignRoamingPoints(const TArray<FTransform> Points)
186{
188
189 for (const FTransform& point : Points)
190 {
191 WayPoints.Add(point.GetLocation());
192 }
193}
194
195void APIDDrone::SetDroneRotation(USkeletalMeshComponent* target, FRotator rotator)
196{
197 if (!target)
198 {
199 return;
200 }
201
202 FRotator currentRotation = target->GetRelativeRotation();
203 FRotator rotationDifference = rotator - currentRotation;
204
205 target->AddRelativeRotation(rotationDifference, false, nullptr, ETeleportType::TeleportPhysics);
206
207 // Prevent locking up when using interp
208 if (FMath::Abs(rotator.Pitch) < 0.001f)
209 {
210 rotator.Pitch = 0.0f;
211 }
212 if (FMath::Abs(rotator.Yaw) < 0.001f)
213 {
214 rotator.Yaw = 0.0f;
215 }
216 if (FMath::Abs(rotator.Roll) < 0.001f)
217 {
218 rotator.Roll = 0.0f;
219 }
220
221 // Corrected rotation
222 target->SetRelativeRotation(rotator, false, nullptr, ETeleportType::TeleportPhysics);
223}
224
226{
227 if (!DroneSkeletalMesh || !ROSTopic || !ROSMessage.IsValid())
228 {
229 return;
230 }
231
232 FVector Start = DroneSkeletalMesh->GetComponentLocation();
233 FVector End = Start - FVector(0, 0, 10000.0f);
234
235 FCollisionQueryParams TraceParams = FCollisionQueryParams(FName(TEXT("UpdateGroundHeight")), true, this);
236 TraceParams.bReturnPhysicalMaterial = false;
237 TraceParams.bTraceComplex = false;
238
239 FHitResult HitResult;
240
241#ifdef ParallelLineTraceSingleByChannel_EXISTS
242 // Defined and implemented in our AGRARSENSE Unreal Engine fork
243 // This LineTrace method doesn't block the physics scene which improves linetrace performance.
244 World->ParallelLineTraceSingleByChannel(
245 HitResult,
246 Start,
247 End,
248 ECC_Visibility,
249 TraceParams,
250 FCollisionResponseParams::DefaultResponseParam);
251#else
252 // If not using our fork of the engine, use default LineTrace method.
253 World->LineTraceSingleByChannel(
254 HitResult,
255 Start,
256 End,
257 ECC_Visibility,
258 TraceParams,
259 FCollisionResponseParams::DefaultResponseParam);
260#endif
261
262 if (HitResult.IsValidBlockingHit())
263 {
264 float Height = (Start.Z - HitResult.ImpactPoint.Z) / 100.0f;
265 float HeightRounded = FMath::RoundToFloat(Height * 1000.0f) / 1000.0f;
266
267 if (!FMath::IsNearlyEqual(DroneHeightFromGround, HeightRounded, 0.005f))
268 {
269 DroneHeightFromGround = HeightRounded;
270
272 ROSTopic->Publish(ROSMessage);
273
274 //FString HitActorName = HitResult.GetActor() ? HitResult.GetActor()->GetName() : TEXT("None");
275 //UE_LOG(LogTemp, Log, TEXT("Drone is %f meters above ground. Hit object: %s"), DroneHeightFromGround, *HitActorName);
276 }
277 }
278}
279
280TArray<FTransform> APIDDrone::GenerateRoamingPoints(float radius, int32 roamingPoints)
281{
282 TArray<FTransform> generatedRoamingPoints;
283 generatedRoamingPoints.Reserve(roamingPoints);
284
285 FVector currentPosition = this->GetTransform().GetLocation();
286
287 FVector min = currentPosition - FVector(radius / 2, radius / 2, 0);
288 FVector max = currentPosition + FVector(radius / 2, radius / 2, 0);
289
290 for (int32 i = 0; i < roamingPoints; i++)
291 {
292 FTransform randomPoint;
293 randomPoint.SetLocation(FVector(FMath::RandRange(min.X, max.X), FMath::RandRange(min.Y, max.Y), 5000));
294
295 generatedRoamingPoints.Add(randomPoint);
296#if WITH_EDITOR
297 UE_LOG(LogTemp, Warning, TEXT("Waypoint %i: (%s)"), i, *randomPoint.GetLocation().ToString());
298#endif
299 }
300
301 return generatedRoamingPoints;
302}
303
305{
306 switch (ROSState)
307 {
309 CreateTopic();
310 break;
311
313 DestroyTopic();
314 break;
315 }
316}
317
319{
320 if (ROSTopic)
321 {
322 return;
323 }
324
325 UROSIntegrationGameInstance* ROSInstance = UAgrarsenseStatics::GetROSGameInstance(GetWorld());
326 if (ROSInstance && ROSInstance->IsROSConnected())
327 {
328 ROSTopic = NewObject<UTopic>(UTopic::StaticClass());
329 if (ROSTopic)
330 {
331
332 FString TopicName = FString::Printf(TEXT("/agrarsense/out/vehicles/%s/height"), *GetActorID_Implementation());
333 //UE_LOG(LogTemp, Warning, TEXT("TopicName is %s"), *TopicName);
334
335 ROSTopic->Init(ROSInstance->ROSIntegrationCore, TopicName, "std_msgs/Float32");
336 ROSTopic->Advertise();
337 }
338 }
339}
340
342{
343 if (ROSTopic)
344 {
345 ROSTopic->Unadvertise();
346 ROSTopic->Unsubscribe();
347 ROSTopic->MarkAsDisconnected();
348 ROSTopic->ConditionalBeginDestroy();
349 ROSTopic = nullptr;
350 }
351}
EROSState
Definition: ROSState.h:16
TSharedPtr< ROSMessages::std_msgs::Float32 > ROSMessage
Definition: PIDDrone.h:162
void CreateTopic()
Definition: PIDDrone.cpp:318
bool waypointReached
Definition: PIDDrone.h:223
TArray< FVector > WayPoints
Definition: PIDDrone.h:218
void AssignRoamingPoints(const TArray< FTransform > Points)
Definition: PIDDrone.cpp:185
void SetDroneRotation(USkeletalMeshComponent *target, FRotator rotator)
Definition: PIDDrone.cpp:195
FVector GetCurrentWaypointTarget()
Definition: PIDDrone.cpp:174
float DroneHeightFromGround
Definition: PIDDrone.h:231
void MoveDroneToPosition(const FTransform Transform)
Override all drone roaming points and continue towards this position.
Definition: PIDDrone.cpp:161
UWorld * World
Definition: PIDDrone.h:207
void AutoPilot(const float DeltaTime)
Definition: PIDDrone.cpp:80
int32 passedWaypoints
Definition: PIDDrone.h:229
FVector StartingPosition
Definition: PIDDrone.h:209
virtual void BeginPlay() override
Definition: PIDDrone.cpp:29
void UpdateGroundHeight()
Definition: PIDDrone.cpp:225
void ClearWaypoints()
Definition: PIDDrone.h:97
UTopic * ROSTopic
Definition: PIDDrone.h:160
void SetFlightpath()
Called in tick function for drone roaming through points.
Definition: PIDDrone.cpp:90
void ROSBridgeStateChanged(EROSState ROSState) override
Definition: PIDDrone.cpp:304
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
Definition: PIDDrone.cpp:61
void DestroyTopic()
Definition: PIDDrone.cpp:341
FDroneParameters DroneParameters
Definition: PIDDrone.h:201
bool IsRoaming() const
Definition: PIDDrone.h:187
UStaticMeshComponent * PositionMesh
Definition: PIDDrone.h:215
USkeletalMeshComponent * DroneSkeletalMesh
Definition: PIDDrone.h:212
TArray< FTransform > GenerateRoamingPoints(float radius, int32 roamingPoints)
Generates a roadming points array for the drone in radius.
Definition: PIDDrone.cpp:280
virtual void Tick(float DeltaTime) override
Definition: PIDDrone.cpp:69
virtual FString GetActorID_Implementation() const override
Definition: Vehicle.h:195
FText InteractableName
Definition: Vehicle.h:258
bool IsVehicleInGarage() const
Definition: Vehicle.h:124
FString ActorID
Definition: Vehicle.h:300
ATransformSensor * TransformSensor
Definition: Vehicle.h:288
static UROSIntegrationGameInstance * GetROSGameInstance(const UObject *WorldContextObject)
static ATransformSensor * SpawnTransformSensor(const FTransform &transform, FTransformSensorParameters Parameters, const FString sensorIdentifier, const FString sensorName, bool SimulateSensor=true, AActor *Parent=nullptr)
EDroneEndAction DroneEndAction
EDroneAction DroneAction
TArray< FTransform > Points
UPrimitiveComponent * PrimitiveComponent