Using UE 4.20.3 with the ROSIntegration plugin, we are creating a simulation for an UGV that we have. The basic premise is that we send ROS messages to the plugin and it handles what to do in the simulation side. We started with a BP model of the vehicle, but in order to speed up the simulation, we migrated the code to a C++ model. We had a separate actor that had the ROS interaction code that we just made a child actor component to both the BP and C++ models. So basically, the ROS actor gets the messages, it processes them and proxies them to the vehicle code to act upon. In order to dispatch an incoming message, we were using a Dynamic Multicast Delegate that the vehicles would bind to. However, after a certain period of time, the delegate reports that it is unbound. We cant figure out why. We’ve put UPROPERTYs and UFUNCTIONs on everything we can think of, but for some reason it’s still becoming unbound. We have a hunch that it’s the GC trashing things when we don’t want it to, but we have no idea what it is trashing. Any help would be appreciated, and I would be happy to provide more info if I’ve left off information.
Code snippets (pruned down to (hopefully) the relevant information)):
ROS_actor.h
// ... outside our class declaration ...
// Creates our Twist Message Dispatcher Struct
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnTwistMsgDispatched, const FVector&, linear, const FVector&, angular);
// ... inside our class delcaration ...
public:
// Activate our Twist Subscriber Function
UFUNCTION(BlueprintCallable, Category="Twist Subscriber")
virtual void twistMsgSub();
// Twist Topic name
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="ROSCOM", meta=(DisplayName="Twist Subscriber Topic"))
FString twist_sub_topic_name_;
// Delegate for twist messages
UPROPERTY(BlueprintAssignable, Category="ROSCOM", meta=(DisplayName="Twist Message Dispatcher"))
FOnTwistMsgDispatched twist_msg_dispatcher_;
private:
// Topics for subscribers!
// These need to be property'd so that they don't get garbage collected
// https://github.com/code-iai/ROSIntegration/issues/32
UPROPERTY()
UTopic* twist_topic_;
ROS_actor.cpp
void ARosActor::twistMsgSub()
{
UROSIntegrationGameInstance* rosinst = Cast<UROSIntegrationGameInstance>(GetGameInstance());
twist_topic_ = NewObject<UTopic>(UTopic::StaticClass());
twist_topic_->Init(rosinst->ROSIntegrationCore, twist_sub_topic_name_, TEXT("geometry_msgs/Twist"), 5);
// Create a std::function callback object
std::function<void(TSharedPtr<FROSBaseMsg>)> TwistCallback = &](TSharedPtr<FROSBaseMsg> msg) -> void
{
auto Concrete = StaticCastSharedPtr<ROSMessages::geometry_msgs::Twist>(msg);
if (Concrete.IsValid())
{
//Unload the data
FVector linear = FVector(Concrete->linear.x, Concrete->linear.y, Concrete->linear.z);
FVector angular = FVector(Concrete->angular.x, Concrete->angular.y, Concrete->angular.z);
if (twist_msg_dispatcher_.IsBound())
{
twist_msg_dispatcher_.Broadcast(linear, angular);
}
}
};
// Subscribe to the topic
twist_topic_->Subscribe(TwistCallback);
}
Vehicle.h
// .. inside our class declaration ...
private:
UFUNCTION()
void onTwist(const FVector& linear, const FVector& angular);
UPROPERTY()
UChildActorComponent *ros_comm_;
Vehicle.cpp
// .. inside BeginPlay() ...
// Bind our function to the Twist Message Dispatcher, so we get the twist messages.
ARosActor* ros_actor = dynamic_cast<ARosActor*>(ros_comm_->GetChildActor());
if (ros_actor != nullptr)
{
ros_actor->twist_msg_dispatcher_.AddDynamic(this, &AHusky::onTwist);
}
// .. outside BeginPlay() ...
// Our Twist message listener.
// This is hooked-up in BeginPlay
void AVehicle::onTwist(const FVector& linear, const FVector& angular)
{
this->linear_ = linear;
this->angular_ = angular;
this->is_accelerating_ = true;
clr_cmd_vel_.PlayFromStart(); // Start the timeline to clear these values after a little bit
// NOTE: This timeline has a single event that, after a little bit, clears the linear_, angular_, and is_accelerating_ values.
}