How to stop the robot by ctrl+c in cpp?

Hello All,

I was doing the course project and tried several ways to stop the robot by ctrl+c but it didn’t work. I know we use rospy.on_shutdown in python to shut down the robot but it didn’t work in the same way when I used ros::shutdown() in cpp. Appreciate anyone who could help me!

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>

class cmdVelPub {

protected:
ros::NodeHandle nh;
ros::Publisher pub;
geometry_msgs::Twist move;
float x = 0.3;
float z = 0.5;

public:
cmdVelPub();
void moveRobot(std::string direction);
};

cmdVelPub::cmdVelPub() {
nh = ros::NodeHandle("~");
pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel", 1);
}

void cmdVelPub::moveRobot(std::string direction) {

if (direction == "front") {
    move.linear.x = x;
    move.angular.z = 0.0;
} else if (direction == "left") {
    move.linear.x = 0.0;
    move.angular.z = z;
} else if (direction == "right") {
    move.linear.x = 0.0;
    move.angular.z = -z;
} else if (direction == "backward") {
    move.linear.x = -x;
    move.angular.z = 0.0;
} else if (direction == "stop") {
    move.linear.x = 0.0;
    move.angular.z = 0.0;
}
pub.publish(move);

}

int main(int argc, char** argv) {
ros::init(argc, argv, "cmdVelPub_node");
cmdVelPub *cmdVelPubObject = new cmdVelPub();
ros::Rate rate(2);

while (ros::ok()) {
    cmdVelPubObject->moveRobot("front");
    rate.sleep();
    ros::spinOnce();
    
}
if (!ros::ok()) {
    cmdVelPubObject->moveRobot("stop");
}
delete cmdVelPubObject;
return 0;

}

Blockquote

`

1 Like

Hi, a node can be shutdown with Ctrl+c in c++ because of the SIGINT handler, and that happens when ros::ok() returns false. In your code, it means that the while loop will drop out as soon as you hit Ctrl+c, so your thinking is correct here.

Have you tried removing the if (!ros::ok()){} and just send the stop command as soon as the loop exits?

@roalgoal

Hello, thanks for your replying. I tried to remove the if (!ros::ok()) statement but the robot is still keep moving. It seems that the node is not killed completely. Could you provide any further suggestions? Thanks!

Hi, you might have to create a custom SIGINT Handler. To do that you can do something like

void mySigintHandler(int sig)
{
    // Do custom action, like publishing stop msg
    ros::shutdown();
}

and in your main, include

int main(int argc, char** argv) {
   singal(SIGINT, mySigintHandler);
}

@roalgoal

Thanks for your replying again. The following lines show how my code looks like:

cmdVelPub *cmdVelPubObject1 = new cmdVelPub();

void mySigintHandler(int sig) {

ROS_INFO("Stop Robot"); 
cmdVelPubObject1->moveRobot("stop");
delete cmdVelPubObject1;
ros::shutdown();

}

int main(int argc, char** argv) {

ros::init(argc, argv, "cmdVelPub_node");
cmdVelPub *cmdVelPubObject2 = new cmdVelPub();
ros::Rate rate(2);
signal(SIGINT, mySigintHandler);

while (ros::ok()) {
     cmdVelPubObject2->moveRobot("front");
     ROS_INFO("Move Robot"); 
     rate.sleep();
     ros::spinOnce();
    
 }
delete cmdVelPubObject2;
return 0;

}

There is no compile error for this code but there is an error saying that I must declare ros::init() before using the NodeHandler. But I can only declare the ros::init() inside the main() . Therefore I am confused how to use the class within the mySignHandler() function. Could you provide some solution to this project? I am really struggling to debug the code for these days. Appreciate a lot!

if (!ros::ok()) {
    cmdVelPubObject->moveRobot("stop");
}

@Zilin_Li
In theory, in your code above, the “if ros is not ok” statement after the while loop is unnecessary because once the while loop is exited, ros is not okay! So you can just remove the if statement and have something like this immediately after the while loop:

cmdVelPubObject->moveRobot("stop");

But even this might not work as ros::ok() only returns false after the node has finished shutting down (and might not be able to stop your robot). So I suggest you try ros::isShuttingDown(), which returns false when the node starts shutting down. Do that check within the while loop itself:

while (ros::ok()) {
    cmdVelPubObject->moveRobot("front");
    rate.sleep();
    ros::spinOnce();
    if (ros::isShuttingDown()) {
       // stop the robot
    }
}

Reference: roscpp/Overview/Initialization and Shutdown - ROS Wiki

@bayodesegun

Thanks for your replying. I try to implement the code above but the robot is still not able to stop. This is my code:

int main(int argc, char** argv) {

ros::init(argc, argv, "cmdVelPub_node");
cmdVelPub *cmdVelPubObject = new cmdVelPub();
ros::Rate rate(2);

while (ros::ok()) {

    ROS_INFO("Move Robot");
    cmdVelPubObject->moveRobot("front");
    rate.sleep();
    ros::spinOnce();
    
    if (ros::isShuttingDown()) {

        ROS_INFO("Stop Robot"); 
        cmdVelPubObject->moveRobot("stop");
    }
}
delete cmdVelPubObject;
return 0;

}

Could you open my package at the backend to have a further look at it? I would appreciate a lot for your time!

Hi
also i think you need to have this in:
ros::init(argc, argv, "cmdVelPub_node", ros::init_options::NoSigintHandler);
this will prevent the auto generated nodehandler or something, and then it will use the code in the
void mySigintHandler(int sig) {

https://wiki.ros.org/roscpp/Overview/Initialization%20and%20Shutdown

i am having simmilar problem, i am trying to impliment the code simmilar to how the shutdownhook works in python but i cant doo it, for some odd reason will post a longer replay later

Ziga

Hi, please share your code here through bitbucket or github so I can take a look. It’s hard to follow your code with the format you are sharing it in