I am quite a newbie with ROS and I am trying to use the actionserver as a nodelet or at least in a package with a nodelet. Is that even possible? If not, what is the right way to run an actionServer without the need for a main func to invoke?
I have tried to use the following tutorial and modify it into a nodelet, but could not see hits.
here is the class I wrote for testing:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "std_msgs/Float32MultiArray.h"
#include <vector>
#include "my_action_server.h"
//Nodelet dependencies
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(my_local_mission_manager::myMissionActionServer, nodelet::Nodelet)
namespace my_local_mission_manager
{
void myMissionActionServer::onInit()
{
ROS_INFO_STREAM("Starting my Mission Action Server Log Node");
}
myMissionActionServer::myMissionActionServer(std::string missionName) : mActionServer(mNH, missionName, boost::bind(&myMissionActionServer::executeMissionCallBack, this, _1), false),
mActionName(missionName)
{
mActionServer.start();
}
myMissionActionServer::~myMissionActionServer(void)
{
ROS_INFO_STREAM("Done - MissionAction");
}
void myMissionActionServer::executeMissionCallBack(const my_local_mission_manager::missionGoalConstPtr &goal)
{
// helper variables
int start = 0;
ros::Rate r(1);
bool success = true;
for (int i = 0; i <= goal->demo_goal; i++)
{
if (mActionServer.isPreemptRequested() || !ros::ok())
{
ROS_INFO("%s: Preempted", mActionName.c_str());
// set the action state to preempted
mActionServer.setPreempted();
success = false;
break;
}
mFeedback.demo_feedback = i;
mActionServer.publishFeedback(mFeedback); //publish the feedback
r.sleep(); // this sleep is not necessary - debug and demo purpose
}
if (success)
{
mResult.demo_result = mFeedback.demo_feedback;
ROS_INFO("%s: Succeeded", mActionName.c_str());
// set the action state to succeeded
mActionServer.setSucceeded(mResult);
}
};
}
The initialization of nodelets are different from nodes. A major point of the tutorial Porting nodes to nodelets is move code from constructor to onInit(). Starting an action server in the constructor is not possible since ROS is not initialized correctly.
To fix your code using C++11, you first need to declare the onInit and a (unique) pointer mActionServer of your action server inside your header:
public:
virtual void onInit() override;
private:
std::unique_ptr<actionlib::SimpleActionServer<my_local_mission_manager::MissionAction>> mActionServer;
Instead of initializing this action server in the constructor, you now need to do this during the official initialization by usage of the getNodeHandle method. Remove all the code from your constructor and initialize your nodelet like
void MyMissionActionServer::onInit()
{
ROS_INFO_STREAM("Starting my Mission Action Server Log Node");
ros::NodeHandle nh = getNodeHandle();
mActionServer = std::unique_ptr<actionlib::SimpleActionServer<my_local_mission_manager::MissionAction>>(
new actionlib::SimpleActionServer<my_local_mission_manager::MissionAction>(
nh, mActionName.c_str(), std::bind(&MyMissionActionServer::executeMissionCallBack, this, std::placeholders::_1), false));
mActionServer->start();
}
The pointer to the action server is needed because the initialization is not done inside the constructor. Because of this you also need to update all action server calls of your existing code. As an example, you need to change mActionServer.setSucceeded(mResult); to mActionServer->setSucceeded(mResult);.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With