Agrarsense
TransformSensor.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
6#include "TransformSensor.h"
7
11
12#include "GeoReferencingSystem.h"
13#include "Kismet/GameplayStatics.h"
14#include "Engine/World.h"
15
16ATransformSensor::ATransformSensor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
17{
18 // TickManager handles this Actor ticking
19 PrimaryActorTick.bCanEverTick = false;
20}
21
22void ATransformSensor::Init(FTransformSensorParameters Params, bool SimulateSensor)
23{
24 SetSimulateSensor(SimulateSensor);
25 Parameters = Params;
26
27 ROSMessages::geometry_msgs::Transform transformMsg;
28 TransformMessage = MakeShared<ROSMessages::geometry_msgs::Transform>(transformMsg);
29
30 GnssMessageString = MakeShared<ROSMessages::std_msgs::String>();
31
32 NavSatFixMessage = MakeShared<ROSMessages::sensor_msgs::NavSatFix>();
33 NavSatFixMessage->header.frame_id = "world";
34 NavSatFixMessage->position_covariance = {
35 1.0, 0, 0,
36 0, 1.0, 0,
37 0, 0, 1.0
38 };
39 NavSatFixMessage->position_covariance_type = NavSatFixMessage->CovarianceType::COVARIANCE_TYPE_DIAGONAL_KNOWN;
40 NavSatFixMessage->status.status = NavSatFixMessage->status.STATUS_FIX;
41 NavSatFixMessage->status.service = NavSatFixMessage->status.SERVICE_GPS;
42
45}
46
48{
49 Super::BeginPlay();
50
51 SetActorEnableCollision(false);
52
53 TickEntry = ATickManager::AddTick(this, BindTick(this, &ATransformSensor::TickParallel), ETickType::LateTickParallel);
54}
55
56void ATransformSensor::EndPlay(const EEndPlayReason::Type EndPlayReason)
57{
59
61
63 TransformMessage.Reset();
64 GnssMessageString.Reset();
65 NavSatFixMessage.Reset();
66
67 Super::EndPlay(EndPlayReason);
68}
69
71{
72 Super::CreateROSTopic();
73
74 GeoReferencingSystem = AGeoReferencingSystem::GetGeoReferencingSystem(GetWorld());
75
76 // If map has been georeferenced, create gnss and navsatfix topics.
78 {
79 FString BaseTopicName = FString::Printf(TEXT("/agrarsense/out/sensors/%s"), *GetSensorIdentifier());
80 FString GnssTopicName;
81 FString NavSatFixTopicName;
82
83 int32 LastSlashIndex = BaseTopicName.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromEnd);
84 if (LastSlashIndex != INDEX_NONE)
85 {
86 GnssTopicName = BaseTopicName.Left(LastSlashIndex) + TEXT("/gnss");
87 NavSatFixTopicName = BaseTopicName.Left(LastSlashIndex) + TEXT("/navsatfix");
88 }
89 else
90 {
91 GnssTopicName = BaseTopicName + TEXT("/gnss");
92 NavSatFixTopicName = BaseTopicName + TEXT("/navsatfix");
93 }
94
95 GnssTopic = NewObject<UTopic>(UTopic::StaticClass());
96 if (GnssTopic)
97 {
98 GnssTopic->Init(ROSInstance->ROSIntegrationCore, GnssTopicName, TEXT("std_msgs/String"));
99 GnssTopic->Advertise();
100 }
101
102 NavSatFixTopic = NewObject<UTopic>(UTopic::StaticClass());
103 if (NavSatFixTopic)
104 {
105 NavSatFixTopic->Init(ROSInstance->ROSIntegrationCore, NavSatFixTopicName, TEXT("sensor_msgs/NavSatFix"));
106 NavSatFixTopic->Advertise();
107 }
108 }
109}
110
112{
113 if (GnssTopic)
114 {
115 GnssTopic->Unadvertise();
116 GnssTopic->Unsubscribe();
117 GnssTopic->MarkAsDisconnected();
118 GnssTopic->ConditionalBeginDestroy();
119 GnssTopic = nullptr;
120 }
121
122 if (NavSatFixTopic)
123 {
124 NavSatFixTopic->Unadvertise();
125 NavSatFixTopic->Unsubscribe();
126 NavSatFixTopic->MarkAsDisconnected();
127 NavSatFixTopic->ConditionalBeginDestroy();
128 NavSatFixTopic = nullptr;
129 }
130
131 if (ROSTopic)
132 {
133 ROSTopic->Unadvertise();
134 ROSTopic->Unsubscribe();
135 ROSTopic->MarkAsDisconnected();
136 ROSTopic->ConditionalBeginDestroy();
137 ROSTopic = nullptr;
138 }
139}
140
142{
143 if (!CanSimulateSensor())
144 {
145 return;
146 }
147
148 // Determine current transform
149 FTransform CurrentTransform;
150 const bool HasValidTransform =
151 (Parameters.UseOwningActorTransform && Parameters.OwningActor && (CurrentTransform = Parameters.OwningActor->GetTransform(), true)) ||
152 (Parameters.PrimitiveComponent && (CurrentTransform = Parameters.PrimitiveComponent->GetRelativeTransform(), true));
153
154 if (!HasValidTransform)
155 {
156 return;
157 }
158
159 const FVector CurrentPosition = CurrentTransform.GetLocation();
160 const FQuat CurrentRotation = CurrentTransform.GetRotation();
161
162 const float PositionToleranceSq = 0.01f;
163 const float RotationTolerance = 0.001f;
164
165 const bool PositionChanged = FVector::DistSquared(CurrentPosition, PreviousPosition) > PositionToleranceSq;
166 const bool RotationChanged = !CurrentRotation.Equals(PreviousRotation, RotationTolerance);
167
168 if (PositionChanged || RotationChanged)
169 {
170 PreviousPosition = CurrentPosition;
171 PreviousRotation = CurrentRotation;
172
173 if (IsROSConnected())
174 {
175 SendTransformDataToROS(CurrentPosition, CurrentRotation);
176 SendGpsDataToROS(CurrentPosition);
177 }
178
179 SaveTransformMetaDataToDisk(CurrentPosition, CurrentRotation);
180 }
181}
182
183void ATransformSensor::SendTransformDataToROS(const FVector& Translation, const FQuat& Rotation)
184{
185 UTopic* Topic = GetROSTopic();
186 if (Topic && TransformMessage.IsValid())
187 {
188 TransformMessage->translation = Translation;
189 TransformMessage->rotation = Rotation;
190
191 Topic->Publish(TransformMessage);
192 }
193}
194
195void ATransformSensor::SendGpsDataToROS(const FVector& ActorPosition)
196{
198 {
199 return;
200 }
201
202 // Convert Actor position to geographic coordinates
203 FGeographicCoordinates GeographicCoordinates = UCoordinateConversionUtilities::UnrealToGeographicCoordinates(GeoReferencingSystem, ActorPosition);
204 const double Latitude = GeographicCoordinates.Latitude;
205 const double Longitude = GeographicCoordinates.Longitude;
206 const double Altitude = GeographicCoordinates.Altitude;
207
208 // gps data as string topic: Latitude, Longitude, Altitude
209 if (GnssTopic && GnssMessageString.IsValid())
210 {
211 const FString GnssDataString = FString::Printf(TEXT("%f, %f, %f"), Latitude, Longitude, Altitude);
212 GnssMessageString->_Data = GnssDataString;
214 }
215
216 // gps data as NavSatFix ROS message
217 if (NavSatFixTopic && NavSatFixMessage.IsValid())
218 {
219 NavSatFixMessage->latitude = Latitude;
220 NavSatFixMessage->longitude = Longitude;
221 NavSatFixMessage->altitude = Altitude;
222 NavSatFixMessage->header.time = FROSTime::Now();
223
225 }
226}
227
228void ATransformSensor::SaveTransformMetaDataToDisk(const FVector& ActorPosition, const FQuat& Rotation)
229{
231 {
232 return;
233 }
234
235 const FRotator ActorRotation = Rotation.Rotator();
236
237 FString MetaData;
238
239 FString TimeStamp = CreateTimeStampString();
240
242 {
243 FGeographicCoordinates GeographicCoordinates = UCoordinateConversionUtilities::UnrealToGeographicCoordinates(GeoReferencingSystem, ActorPosition);
244 const double Latitude = GeographicCoordinates.Latitude;
245 const double Longitude = GeographicCoordinates.Longitude;
246 const double Altitude = GeographicCoordinates.Altitude;
247
248 MetaData = FString::Printf(TEXT("%s, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.8f, %.8f, %.8f"), *TimeStamp, ActorPosition.X, ActorPosition.Y, ActorPosition.Z,
249 ActorRotation.Pitch, ActorRotation.Yaw, ActorRotation.Roll,
250 GeographicCoordinates.Latitude, GeographicCoordinates.Longitude, GeographicCoordinates.Altitude);
251 }
252 else
253 {
254 MetaData = FString::Printf(TEXT("%s, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f"), *TimeStamp, ActorPosition.X, ActorPosition.Y, ActorPosition.Z,
255 ActorRotation.Pitch, ActorRotation.Yaw, ActorRotation.Roll);
256 }
257
258 // Write to the log file (this is written after sensor is destroyed)
259 WriteToLogFile(MetaData);
260}
261
263{
265 {
266 return;
267 }
268
270
271 FLogFileSettings Settings;
274 Settings.QueueLength = MAX_int32; // Only write the log after destroying the sensor
275 Settings.KeepFileOpen = false;
276 Settings.Timestamp = false;
277 Settings.OverrideFilePath = true;
278 Settings.FilePath = FileSavePath;
279
280 LogFile = NewObject<ULogFile>(ULogFile::StaticClass());
281 if (IsValid(LogFile))
282 {
283 FString Prefix;
285 {
286 Prefix = Vehicle->GetActorID_Implementation();
287 }
288 FString FileName = Prefix.IsEmpty() ? TEXT("transform") : Prefix + TEXT("_transform");
289
290 LogFile->Create(FileName, Settings);
291
292 AGeoReferencingSystem* GeoRef = AGeoReferencingSystem::GetGeoReferencingSystem(GetWorld());
293 if (GeoRef)
294 {
295 WriteToLogFile("timestamp, X location, Y location, Z location, yaw rotation, pitch rotation, roll rotation, GPS latitude, GPS longitude, GPS altitude");
296 }
297 else
298 {
299 WriteToLogFile("timestamp, X location, Y location, Z location, yaw rotation, pitch rotation, roll rotation");
300 }
301 }
302}
303
305{
306 if (!FileSavePath.IsEmpty())
307 {
308 return;
309 }
310
311 const FString DataLocation = UAgrarsensePaths::GetDataFolder();
312
313 // Default save path
314 FileSavePath = DataLocation + GetActorID_Implementation() + "/";
315
317 {
318 // If sensor is attached to vehicle, save data to vehicle folder
319 const FString VehicleName = Vehicle->GetActorID_Implementation();
320 FileSavePath = DataLocation + VehicleName;
321 }
322 else if (ParentActor)
323 {
324 if (ASensor* ParentSensor = Cast<ASensor>(ParentActor))
325 {
326 if (AVehicle* SensorVehicle = ParentSensor->IsAttachedToVehicle())
327 {
328 // If this TransformSensor is attached to a sensor that's attached to a vehicle,
329 // save data to Vehicle/Sensor folder
330 const FString VehicleName = SensorVehicle->GetActorID_Implementation();
331 const FString ParentSensorName = ParentSensor->GetActorID_Implementation();
332 FileSavePath = DataLocation + VehicleName + "/" + ParentSensorName;
333 }
334 }
335 }
336}
static auto BindTick(ObjectType *Object, FunctionType Function)
Definition: TickManager.h:162
Definition: Sensor.h:45
UROSIntegrationGameInstance * ROSInstance
Definition: Sensor.h:375
bool CanSimulateSensor() const
Definition: Sensor.h:170
FString GetSensorIdentifier() const
Definition: Sensor.h:75
UTopic * ROSTopic
Definition: Sensor.h:361
AActor * ParentActor
Definition: Sensor.h:370
void SetSimulateSensor(bool SimulateSensor)
Definition: Sensor.h:160
UTopic * GetROSTopic() const
Definition: Sensor.h:150
AVehicle * IsAttachedToVehicle() const
Definition: Sensor.cpp:152
ULogFile * LogFile
Definition: Sensor.h:367
virtual FString GetActorID_Implementation() const override
Definition: Sensor.h:216
FString CreateTimeStampString() const
Definition: Sensor.cpp:322
FString FileSavePath
Definition: Sensor.h:372
void WriteToLogFile(const FString &Message)
Definition: Sensor.cpp:294
FORCEINLINE bool IsROSConnected() const
Definition: Sensor.h:201
static void RemoveTick(FTickEntry TickEntry)
Definition: TickManager.cpp:80
static FTickEntry AddTick(UObject *Object, std::function< void(float)> Function, ETickType Type)
Definition: TickManager.cpp:51
void SendTransformDataToROS(const FVector &Translation, const FQuat &Rotation)
void CreateDataSavePath() override
virtual void BeginPlay() override
TSharedPtr< ROSMessages::std_msgs::String > GnssMessageString
void SendGpsDataToROS(const FVector &ActorPosition)
UTopic * NavSatFixTopic
TSharedPtr< ROSMessages::geometry_msgs::Transform > TransformMessage
void SaveTransformMetaDataToDisk(const FVector &Translation, const FQuat &Rotation)
void CreateLogFile() override
TSharedPtr< ROSMessages::sensor_msgs::NavSatFix > NavSatFixMessage
virtual void CreateROSTopic() override
FVector PreviousPosition
FTransformSensorParameters Parameters
virtual void DestroyROSTopic() override
AGeoReferencingSystem * GeoReferencingSystem
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
FTickEntry TickEntry
void TickParallel(float DeltaTime)
void Init(FTransformSensorParameters Params, bool SimulateSensor=true)
ATransformSensor(const FObjectInitializer &ObjectInitializer)
static FString GetDataFolder()
static FGeographicCoordinates UnrealToGeographicCoordinates(AGeoReferencingSystem *GeoReferencingSystem, const FVector &Position)
void Create(const FString &FileNameWithoutExtension, FLogFileSettings Settings)
Definition: LogFile.cpp:40
bool KeepFileOpen
Definition: LogFile.h:42
bool Timestamp
Definition: LogFile.h:39
FString FilePath
Definition: LogFile.h:54
FFileWriteOptions FileWriteOptions
Definition: LogFile.h:45
int32 QueueLength
Definition: LogFile.h:48
bool OverrideFilePath
Definition: LogFile.h:51
FFileCreationOptions FileCreationOptions
Definition: LogFile.h:36
UPrimitiveComponent * PrimitiveComponent