ROS Mini Challenge #4 – Controlling a drone with ROS Actions

ROS Mini Challenge #4 – Controlling a drone with ROS Actions

 

 

 

 

 

 

 

 

 

What we are going to learn

Learn how to create a ROS Action message and compile it for C++, to control a drone.

 

List of resources used in this post

Opening the ROSject

Once you clicked in the link to get a copy of the ROSject,

When you click in the ROSject Link (www.rosject.io/l/e80ec70/), you will get a copy of it. You can then download it if you want to use it on our local computer, or you can just click Open to have it opened in ROSDS.
Once it’s open, you can find all the code associated with this challenge in the ~/catkin_ws folder. To see the full code, open the IDE by going to the top menu and select Tools->IDE.

Code Editor (IDE) - ROSDS

Code Editor (IDE) – ROSDS

Launching the simulation

Go to the top menu and select Simulations. On the menu that appears, click on the Choose launch file… button.

Choose lunch file to open simulation in ROSDS

Choose lunch file to open simulation in ROSDS

Now, from the launch files list, select the launch file named rotw4.launch from the drone_construct package.
ROS Mini Challenge #4 - Drone - ROSDS

ROS Mini Challenge #4 – Drone – ROSDS

Finally, click on the Launch button to start the simulation. In the end, you should get something like this:

Drone simulation in ROSDS

Drone simulation in ROSDS

The problem to solve

As you can see, this ROSject contains 1 package inside its catkin_ws workspace: rotw4_code. The purpose of this packages is to allow you to control the Drone (Take Off or Land) using an Action Server.

So, the main purpose of this ROSject is to be able to send a Goal to this Action Server, specifying a keyword in the goal (either “UP” or “DOWN”), and the Drone receives and executes this goal. The steps in order to achieve this are the following:

First, you will need to compile the Action Server and the Action message used. For this, execute the following command:

cd ~/catkin_ws/
catkin_make
source ~/catkin_ws/devel/setup.bash

 

Once, the compilation has finished successfully, you will start the Action Server by executing the following command:

rosrun rotw4_code rotw4_action_node

 

Once the Action Server is up and running, you can send goals to the Action with the following commands.

In order to make the Drone TAKE OFF:

rostopic pub /rotw4_action/goal rotw4_code/ActionMsgActionGoal "header:
  seq: 0
  stamp:
    secs: 0
    nsecs: 0
  frame_id: ''
goal_id:
  stamp:
    secs: 0
    nsecs: 0
  id: ''
goal:
  goal: 'UP'"

You should see the Drone taking off, like in the following image:

Drone taking off (moving up) in ROSDS

Drone taking off (moving up) in ROSDS

And in order to make the Drone LAND, you could use the following command:

rostopic pub /rotw4_action/goal rotw4_code/ActionMsgActionGoal "header:
  seq: 0
  stamp:
    secs: 0
    nsecs: 0
  frame_id: ''
goal_id:
  stamp:
    secs: 0
    nsecs: 0
  id: ''
goal:
  goal: 'DOWN'"

You should see the Drone landing:

Drone landing (moving down) in ROSDS

Drone landing (moving down) in ROSDS

Also, you will see in the Action Server a message indicating that the action was successfully executed.

Action Server succeeded in ROSDS

Action Server succeeded in ROSDS

Ok, so… where’s the problem? If you have tried to reproduce the steps described above you have already seen that it DOES NOT WORK. When you follow the pipeline you can see that even the 1st step, which is to compile the package, it does not work (though it is not the only error here). But Why? Let’s find it in the section below.

Solving the ROS Mini Challenge

According to the instructions, we need to have a look at the rotw4_code package and try to find there the errors.

If you looked carefully, after running the first commands, you had an error:

cd ~/catkin_ws/
catkin_make

The error was:

Building CXX object rotw4_code/CMakeFiles/rotw4_action_node.dir/src/rotw4_action.cpp.o
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:3:34: fatal error: rotw4_code/ActionMsg.h: No such file or directory
compilation terminated.
rotw4_code/CMakeFiles/rotw4_action_node.dir/build.make:62: recipe for target 'rotw4_code/CMakeFiles/rotw4_action_node.dir/src/rotw4_action.cpp.o' failed
make[2]: *** [rotw4_code/CMakeFiles/rotw4_action_node.dir/src/rotw4_action.cpp.o] Error 1
CMakeFiles/Makefile2:1909: recipe for target 'rotw4_code/CMakeFiles/rotw4_action_node.dir/all' failed
make[1]: *** [rotw4_code/CMakeFiles/rotw4_action_node.dir/all] Error 2
Makefile:138: recipe for target 'all' failed
make: *** [all] Error 2
Invoking "make -j2 -l2" failed

The problem is that it can’t find the ActionMsg.h, so, which tell us that our ~/catkin_ws/src/rotw4_code/action/ActionMsg.action is not being compiled. Let’s then open the CMakeLists.txt file located in the rotw4_code package, to see where is the error.

If we look at the line 153 of the CMakeLists.txt file, we find the following:

add_executable(rotw4_action_node src/rotw4_action.cpp)
target_link_libraries(rotw4_action_node
${catkin_LIBRARIES}
)

we didn’t add the dependencies. To solve this compilation part, let’s add the line below right after line 153:

add_dependencies(rotw4_action_node ${rotw4_action_node_EXPORTED_TARGETS}
so, in the end, it will look like:
add_executable(rotw4_action_node src/rotw4_action.cpp)
add_dependencies(rotw4_action_node ${rotw4_action_node_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(rotw4_action_node
${catkin_LIBRARIES}
)

Let’s now try to compile our package again:

cd ~/catkin_ws/
catkin_make

Now we have another error. Although it looks similar, at least now the messages were compiled:

[ 97%] Building CXX object rotw4_code/CMakeFiles/rotw4_action_node.dir/src/rotw4_action.cpp.o
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:3:34: fatal error: rotw4_code/ActionMsg.h: No such file or directory
compilation terminated.

We can confirm the message was generated with find ~/catkin_ws/devel/ -name ActionMsg*.h:

$ find ~/catkin_ws/devel/ -name ActionMsg*.h
/home/user/catkin_ws/devel/include/rotw4_code/ActionMsgActionResult.h
/home/user/catkin_ws/devel/include/rotw4_code/ActionMsgActionFeedback.h
/home/user/catkin_ws/devel/include/rotw4_code/ActionMsgAction.h
/home/user/catkin_ws/devel/include/rotw4_code/ActionMsgFeedback.h
/home/user/catkin_ws/devel/include/rotw4_code/ActionMsgGoal.h
/home/user/catkin_ws/devel/include/rotw4_code/ActionMsgActionGoal.h
/home/user/catkin_ws/devel/include/rotw4_code/ActionMsgResult.h

As we can see, we have no file named ActionMsg.h, which is imported by rotw4_code/src/rotw4_action.cpp. Let’s then fix rotw4_action.cpp on line 3 and import ActionMsgAction.h. Now let’s try to compile again:

cd ~/catkin_ws/
catkin_make

Now we have different errors:

Scanning dependencies of target rotw4_action_node
[ 97%] Building CXX object rotw4_code/CMakeFiles/rotw4_action_node.dir/src/rotw4_action.cpp.o
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:11:33: error: 'ActionMsg' is not a member of 'rotw4_code'
   actionlib::SimpleActionServer<rotw4_code::ActionMsg> as_;
                                 ^
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:11:33: error: 'ActionMsg' is not a member of 'rotw4_code'
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:11:54: error: template argument 1 is invalid
   actionlib::SimpleActionServer<rotw4_code::ActionMsg> as_;
                                                      ^
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp: In constructor 'ActionClass::ActionClass(std::__cxx11::string)':
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:29:26: error: expression list treated as compound expression in mem-initializer [-fpermissive]
         action_name_(name) {
                          ^
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:30:9: error: request for member 'start' in '((ActionClass*)this)->ActionClass::as_', which is of non-class type 'int'
     as_.start();
         ^
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp: In member function 'void ActionClass::executeCB(const ActionMsgGoalConstPtr&)':
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:43:13: error: request for member 'isPreemptRequested' in '((ActionClass*)this)->ActionClass::as_', which is of non-class type 'int'
     if (as_.isPreemptRequested() || !ros::ok()) {
             ^
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:46:11: error: request for member 'setPreempted' in '((ActionClass*)this)->ActionClass::as_', which is of non-class type 'int'
       as_.setPreempted();
           ^
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:62:9: error: request for member 'publishFeedback' in '((ActionClass*)this)->ActionClass::as_', which is of non-class type 'int'
     as_.publishFeedback(feedback_);
         ^
/home/user/catkin_ws/src/rotw4_code/src/rotw4_action.cpp:68:11: error: request for member 'setSucceeded' in '((ActionClass*)this)->ActionClass::as_', which is of non-class type 'int'
       as_.setSucceeded(result_);

We have a bunch of errors, so, let’s look at the first one first, which says:

rotw4_action.cpp:11:33: error: 'ActionMsg' is not a member of 'rotw4_code' actionlib::SimpleActionServer<rotw4_code::ActionMsg> as_;

The problem is almost the same as before. Instead of using ActionMsgAction, we used ActionMsg. Let’s fix that on line 11, and try to compile again:

cd ~/catkin_ws/
catkin_make

Congratulations, now everything compiled as expected:

Scanning dependencies of target rotw4_action_node
[ 97%] Building CXX object rotw4_code/CMakeFiles/rotw4_action_node.dir/src/rotw4_action.cpp.o
[100%] Linking CXX executable /home/user/catkin_ws/devel/lib/rotw4_code/rotw4_action_node
[100%] Built target rotw4_action_node
Scanning dependencies of target rotw4_code_generate_messages
[100%] Built target rotw4_code_generate_messages

Now, let’s run our action server:

rosrun rotw4_code rotw4_action_node

Now we could try to make the robot take off by running the following command in a different web shell:

rostopic pub /rotw4_action/goal rotw4_code/ActionMsgActionGoal "header:
  seq: 0
  stamp:
    secs: 0
    nsecs: 0
  frame_id: ''
goal_id:
  stamp:
    secs: 0
    nsecs: 0
  id: ''
goal:
  goal: 'UP'"

The only problem is that the robot doesn’t move as expected yet, although the action server says succeeded.

rotw4_action: Succeeded

If we look the file rotw4_action.cpp, in the executeCB method, everything seems to be correct.

void executeCB(const rotw4_code::ActionMsgGoalConstPtr &goal) {

    std::string upDown = goal->goal;
    takeoff_pub_ = nh_.advertise<std_msgs::Empty>("/drone/takeoff", 1000);
    land_pub_ = nh_.advertise<std_msgs::Empty>("/drone/land", 1000);

    // check that preempt has not been requested by the client
    if (as_.isPreemptRequested() || !ros::ok()) {
      ROS_INFO("%s: Preempted", action_name_.c_str());
      // set the action state to preempted
      as_.setPreempted();
      success_ = false;
    }

    if (upDown == "UP") {
      takeoff_pub_.publish(takeoff_msg_);
      feedback_.feedback = "Taking Off Drone...";
    }

    if (upDown == "DOWN") {
      land_pub_.publish(land_msg_);
      feedback_.feedback = "Landing Drone...";
    }

    // feedback_.feedback = i;
    // publish the feedback
    as_.publishFeedback(feedback_);

    if (success_) {

      ROS_INFO("%s: Succeeded", action_name_.c_str());
      // set the action state to succeeded
      as_.setSucceeded(result_);
    }
  }

The problem is that we are instantiating the topic publishers and publishing on them straight away, and the topics need some time after being defined to start being used. To solve this, let’s add a while loop to publish at least 4 times, once a second:

void executeCB(const rotw4_code::ActionMsgGoalConstPtr &goal) {

    std::string upDown = goal->goal;
    takeoff_pub_ = nh_.advertise<std_msgs::Empty>("/drone/takeoff", 1000);
    land_pub_ = nh_.advertise<std_msgs::Empty>("/drone/land", 1000);
    ros::Rate rate(1);

    // check that preempt has not been requested by the client
    if (as_.isPreemptRequested() || !ros::ok()) {
      ROS_INFO("%s: Preempted", action_name_.c_str());
      // set the action state to preempted
      as_.setPreempted();
      success_ = false;
    }

    if (upDown == "UP") {
      int i = 0;
      while (i < 4) {
        takeoff_pub_.publish(takeoff_msg_);
        feedback_.feedback = "Taking Off Drone...";
        i++;
        rate.sleep();
      }
    }

    if (upDown == "DOWN") {
      land_pub_.publish(land_msg_);
      feedback_.feedback = "Landing Drone...";
    }

    // feedback_.feedback = i;
    // publish the feedback
    as_.publishFeedback(feedback_);

    if (success_) {

      ROS_INFO("%s: Succeeded", action_name_.c_str());
      // set the action state to succeeded
      as_.setSucceeded(result_);
    }
  }

If we now compile the code again

cd ~/catkin_ws/
catkin_make

run the node:

rosrun rotw4_code rotw4_action_node

and publish the message to take off again:

rostopic pub /rotw4_action/goal rotw4_code/ActionMsgActionGoal "header:
  seq: 0
  stamp:
    secs: 0
    nsecs: 0
  frame_id: ''
goal_id:
  stamp:
    secs: 0
    nsecs: 0
  id: ''
goal:
  goal: 'UP'"

now the robot should work as expected.

Youtube video

So this is the post for today. Remember that we have the live version of this post on YouTube. If you liked the content, please consider subscribing to our youtube channel. We are publishing new content ~every day.

Keep pushing your ROS Learning.

Developing Web Interfaces For ROS Robots #2 – Adding styles to the ROS control web page

Developing Web Interfaces For ROS Robots #2 – Adding styles to the ROS control web page

Hello ROS Developers,

This is the 2nd of 4 posts of the series Developing web interfaces for ROS Robots.

In this post, we are going to add some styles to the web page using a very popular framework: Bootstrap. Furthermore, we will add a JavaScript framework to make our code look better: Vue.js.

 


1 – Loading the libraries

First things first! Let’s import the libraries we are going to use.

In our html file (the only one so far), we add the following code to the <head> section:

<head>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
	 crossorigin="anonymous">
	<script type="text/javascript" src="https://static.robotwebtools.org/roslibjs/current/roslib.min.js">
	</script>
	<script src="https://cdn.jsdelivr.net/npm/vue">
	</script>
</head>

The first tag (<link>) is used to add the Bootstrap framework. It’s a css file, that’s why it looks different than the <script> tags. It will allow us to create prettier elements in our page without having the work to stylish them.

The second tag is the same we have used in the previous post. It includes roslib.js.

Finally, the third tag includes Vue.js framework. This framework will make our JavaScript code quite different, but more organized.

 


2 – New appearance for the page

Let’s create a new page for our project.

We want to have something pretty and easy to understand. In order to do so, let’s create some new HTML elements. Our <body> section will look like below:

<body>
	<div id="app" class="container">
		<div class="jumbotron">
			<h1>Hello from ROSDS!</h1>
		</div>

		<div class="row" style="max-height:200px;">
			<div class="col-md-6">
				<h3>Connection status</h3>

				<p class="text-success">Connected!</p>
				<p class="text-danger">Not connected!</p>

				<label>Websocket server address</label>
                                <input type="text" />
                                <br />
                                <button class="btn btn-danger">Disconnect!</button>
                                <button class="btn btn-success">Connect!</button>
			</div>
			<div class="col-md-6" style="max-height:200px; overflow:auto;">
				<h3>Log messages</h3>
                                <div>
                                </div>
			</div>
		</div>
	</div>
</body>

And the page (make sure you have the webserver running like we did in the previous post) must look like this:

 

We have created many elements and they are being shown all together. Let’s improve it!

 


3 – Adding JavaScript with Vue.js

This time we are creating a separeted file to keep our JavaScript. Create a file main.js in the same folder of the index.html

Our file will contain the following code:

var app = new Vue({
    el: '#app',
    // storing the state of the page
    data: {
        connected: false,
        ros: null,
        ws_address: 'ws://3.91.38.82:9090',
        logs: [],
    },
    // helper methods to connect to ROS
    methods: {
        connect: function() {
            this.logs.unshift('connect to rosbridge server!!')
            this.ros = new ROSLIB.Ros({
                url: this.ws_address
            })
            this.ros.on('connection', () => {
                this.connected = true
                this.logs.unshift('Connected!')
                // console.log('Connected!')
            })
            this.ros.on('error', (error) => {
                this.logs.unshift('Error connecting to websocket server')
                // console.log('Error connecting to websocket server: ', error)
            })
            this.ros.on('close', () => {
                this.connected = false
                this.logs.unshift('Connection to websocker server closed')
                // console.log('Connection to websocket server closed.')
            })
        },
        disconnect: function() {
            this.ros.close()
        },
    },
})

Let’s understand what we have there:

We start instantiating a Vue.js object. This object refers the element #app of the webpage. (The element identified by the attribute id=”app”). It means that everything inside this element can be managed by our JavaScript code.

The next attribute the called data. This attribute is an object that contains other attributes. These are defined for our application, they are custom attributes, not pre-defined by the framework.

We have defined a boolean connected. A ros object, that will handle the connection to rosbridge server. The address of the server, ws_address. And the last one logs, that is an array, which will contain messages of the events.

Then, we have the methods attribute. This is pre-defined by the framework and is the place to define functions to help us developing our application. We have created two: connect and disconnect.

The first one is very similar to what we have done in the previous post, but now interacting with the attributes inside data. We start the connect method creating a new log and pushing to the this.logs array. Then we define the connection object and assign the this.ros to handle it. Finally, the same callbacks we have defined before are defined here, but changing the value of the boolean connected and adding the logs to the array, instead of showing in the console.

The disconnect method is just closing the connection.


4 – Integrating with the webpage

In order to allow the main.js to do its “magic”, we need to adjust the html code. First thing is to include it at the end of the <body> section.

    ...
    <script type="text/javascript" src="main.js">
    </script>
</body>

Then, we need to adjust the elements to be manipulated.

The text message that represents the status of the connection must depend on the connected variable.

<p class="text-success" v-if="connected">Connected!</p>
<p class="text-danger" v-else>Not connected!</p>

The <input> element must fill the variable ws_address and, vice-versa, the value of the variable must update the element:

<input type="text" v-model="ws_address" />

The buttons must trigger the methods we have defined and be shown depending on the connected value:

<button @click="disconnect" class="btn btn-danger" v-if="connected">Disconnect!</button>
<button @click="connect" class="btn btn-success" v-else>Connect!</button>

And we want to iterate the logs array to show the messages we have added on the events changing:

<div class="col-md-6" style="max-height:200px; overflow:auto;">
    <h3>Log messages</h3>
    <div>
        <p v-for="log in logs">
            {{ log }}
        </p>
    </div>
</div>

5 – Testing the webpage

Reload the page and connect to the address you have for rosbridge. Try the disconnect button as well and reconnect.

Check how they behave: connection message, buttons and logs. You must have something similar when you don’t have rosbridge running:

And when you have it running (remember to launch it using roslaunch rosbridge_server rosbridge_websocket.launch):


ROSJect created along the post:

http://www.rosject.io/l/dd94116/

[ROS Mini Challenge] #3 – make a manipulator robot execute a trajectory

[ROS Mini Challenge] #3 – make a manipulator robot execute a trajectory

In this post, we will see how to configure MoveIt to make a manipulator robot execute a trajectory based on a Position Goal. We send a Position Goal to the manipulator, and the robot computes and executes a trajectory plan in order to reach this goal.

PS: This ROS project is part of our ROS Mini Challenge series, which gives you an opportunity to win an amazing ROS Developers T-shirt! This challenge is already solved. For updates on future challenges, please stay tuned to our Twitter channel.

Step 1: Grab a copy of the ROS Project that contains the simulation

Click here to get your own copy of the project. If you don’t have an account on the ROS Development Studio, you will need to create one. Once you create an account or log in, we will copy the project to your workspace. That done, open your ROSject using the Open button. This might take a few moments, please be patient.

You should now see a notebook with detailed instructions about the challenge. Please ignore the Claim your Prize! section because…well…you are late the party 🙂

Step 2: Start the Simulation and run the MoveIt program to configure the manipulator

  1. Click on the Simulations menu and then Choose launch file . In the dialog that appears, select main.launch from the shadow_gazebo package. Then click the Launch button. You should see a Gazebo window popup showing the simulation: a manipulator robot.
  2. Start the MoveIt package – pick a Shell from the Tools menu and run the following command:
user:~$ roslaunch myrobot_moveit_config myrobot_planning_execution.launch

Once you see the green text that says “You can start planning now”, pick the Graphical Tools app from the Tools menu.

Probably, at first, you will see the window a bit displaced.

 

In order to center it, just click on the Resize opened app button. At the end, you should get something like this:

In the window that appears, go to the Planning tab and then to the Select Goal State dropdown under Query.

 

Select “start” and click Update.

 

Click on the Plan button under Commands in order to calculate a suitable trajectory for reaching the goal position.

Finally, click on the Execute button under Commands in order to execute the position goal (and the plan).

You should see the manipulator on the right moving in order to execute the trajectory, as shown below (in the Graphical Tools and the Gazebo windows).

But this didn’t happen did it? Let’s fix that in the next section!

Step 3: Let’s solve the problem!

What the heck was the problem?! Hmm, I wished I had paid more attention to the Shell…because it left a message for us there, in red letters! Let’s go see it:

[ERROR] [1580760680.575679474]: Unable to identify any set of controllers that can actuate the specified joints: [ elbow_joint shoulder_lift_joint shoulder_pan_joint wrist_1_joint wrist_2_joint wrist_3_joint ]
[ERROR] [1580760680.575917519]: Known controllers and their joints:

[ INFO] [1580760680.671585828]: ABORTED: Solution found but controller failed during execution

We didn’t set up a controller for moving the robot arm. Whew!

We need to examine the package to find the fix, so fire up the IDE from the Tools menu and browse to the catkin_ws/src/myrobot_moveit_config package. In the launch subdirectory, we have a file named smart_grasping_sandbox_moveit_controller_manager.launch.xml:

<launch>
  <rosparam file="$(find myrobot_moveit_config)/config/my_controllers.yaml"/>
  <param name="use_controller_manager" value="false"/>
  <param name="trajectory_execution/execution_duration_monitoring" value="false"/>
  <param name="moveit_controller_manager" value="moveit_simple_controller_manager/MoveItSimpleControllerManager"/>
</launch>

The first line of the configuration above refers to a file my_controllers.yamlfile in the config subdirectory let’s open that file up:

controller_list:
    Hi there!

Huh, we expected this file to list the controllers, but all we get here is “Hi there!”! We don’t need that – so this must be the wrong file! We need to find a file containing the controllers we need, so let’s check out some other files there, for example controllers.yaml:

controller_list:
  - name: arm_controller
    action_ns: follow_joint_trajectory
    type: JointTrajectoryController
    joints:
      - shoulder_pan_joint
      - shoulder_lift_joint
      - elbow_joint
      - wrist_1_joint
      - wrist_2_joint
      - wrist_3_joint
  - name: hand_controller
    action_ns: follow_joint_trajectory
    type: FollowJointTrajectory
    joints:
      - H1_F1J1
      - H1_F1J2
      - H1_F1J3
      - H1_F2J1
      - H1_F2J2
      - H1_F2J3
      - H1_F3J1
      - H1_F3J2
      - H1_F3J3

WOW, this might be what we need (ssh! Top secret: it is!), so let’s copy over the contents of controllers to  my_controllers and save!

Step 4: Test the Fix

Now get back to the Shell. Stop the program we ran in Step 2 using Ctrl + C.

^C[rviz_rosdscomputer_8081_1478977940469499802-4] killing on exit
[move_group-3] killing on exit
[joint_state_publisher-2] killing on exit
[rosout-1] killing on exit
[master] killing on exit
shutting down processing monitor...
... shutting down processing monitor complete
done
user:~$ roslaunch myrobot_moveit_config myrobot_planning_execution.launch

After this, repeat everything in Step 2. Oops, yeah, it didn’t work yet!! Instead, we got another error:

[ERROR] [1580771100.563075907, 98.692000000]: Unable to identify any set of controllers that can actuate the specified joints: [ elbow_joint shoulder_lift_joint shoulder_pan_joint wrist_1_joint wrist_2_joint wrist_3_joint ]
[ERROR] [1580771100.563656482, 98.692000000]: Known controllersand their joints:
controller 'hand_controller' controls joints:
  H1_F1J1
  H1_F1J2
  H1_F1J3
  H1_F2J1
  H1_F2J2
  H1_F2J3
  H1_F3J1
  H1_F3J2
  H1_F3J3

The controllers again! Now let’s examine the error message again.

  • The controllers for the parts that need to move could not be found. But these controllers are listed in the my_controllers.yaml .
  • Some other controllers listed in the same file were found.

Perhaps there’s some misconfiguration – let’s have a second look at this my_controllers.yaml file:

  • We have two controllers: arm_controller and hand_controller.
  • They both have an action_ns called follow_joint_trajectory.
  • But they have different types! They should be the same since they have the same “action namespace”!
  • The type for arm_controller is probably wrong because it’s the one we couldn’t find!

Now, change the type for arm_controller to FollowJointTrajectory and repeat step 2! Now the manipulator should execute the trajectory successfully – you should see the robot arm moving in both the Graphical Tools and the Gazebo windows.

This is one example of how to make a manipulator robot execute a trajectory based on a Position Goal. I hope you found it useful.

Extra: Video of this post

We made a video showing how this challenge was solved. If you prefer “sights and sounds” to “black and white”, here you go:

Related Resources

Feedback

Did you like this post? Do you have questions about what was explained? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS or ROS2 topics, please let us know in the comments area and we will do a video or post about it.

[ROS Mini Challenge] #2 – Make a robot navigate to a specific location

[ROS Mini Challenge] #2 – Make a robot navigate to a specific location

 

What we are going to learn

  1. How to make a ROS-based robot navigate using RViz’s 2D Nav Goal
  2. How to solve the ROSject Of The Week Challenge #2, making a robot move to a specific position

List of resources used in this post

  1.  The ROSject of the project with the simulation and the ROS Packages: https://buff.ly/32HGTis
  2.  The ROS Discourse post where the challenge was posted: https://discourse.ros.org/t/rosject-of-the-week-challenge-2/11474
  3. Robot Ignite Academy courses: www.robotigniteacademy.com/en/

What is the ROS Mini Challenge

It’s a ROS-related problem in which the first person who solves wins a prize. Different challenges are published every week.

Launching the Simulation

When you click in the ROSject Link (www.rosject.io/l/e0bc374/), you will get a copy of it. You can then download it if you want to use it on our local computer, or you can just click Open to have it opened in ROSDS.

Open ROSject of the Week number 2 in ROSDS

Open ROSject of the Week number 2 in ROSDS

Once you have it open, go to the top menu and select Simulations. On the menu that appears, click on the Choose launch file button.

Choose lunch file to open simulation in ROSDS

Choose lunch file to open simulation in ROSDS

 

Now, from the launch files list, select the launch file named main.launch from the summit_xl_gazebo package.

Summit XL gazebo in ROSDS, ROSject of the week

Summit XL gazebo in ROSDS, ROSject of the week

Finally, click on the Launch button to start the simulation. In the end, you should get something like this:

Summit XL gazebo simulation in ROSDS, ROSject of the week

Summit XL gazebo simulation in ROSDS, ROSject of the week

The problem to solve

As you can see, this ROSject contains 3 packages inside its catkin_ws workspace: my_summit_mapping, my_summit_localization, and my_summit_path_planning. The purpose of all these packages is to make the Summit XL robot autonomously navigate through the environment.So, the main purpose of this ROSject is to be able to send a Navigation Goal to the robot, and the Summit XL robot computes and executes a trajectory plan in order to reach this goal. The steps in order to achieve this are the following:
First, you launch the Navigation system by executing the following command in the terminal (Tools -> Shell):
source ~/catkin_ws/devel/setup.bash
roslaunch my_summit_path_planning move_base.launch
Second, in a different terminal, you launch RViz in order to visualize all the required elements:
rosrun rviz rviz
Now, in order to be able to visualize the RViz screen, you will need to open the Graphical Tools window, in the Tools menu.

Launching Graphical Tools in ROSDS

Launching Graphical Tools in ROSDS

Also, in order to speed up the process, we have already created an RViz configuration that you can load. To do so, just click on the Open Config button in RViz, and select the RViz configuration in the following path: /home/user/catkin_ws/src/my_summit_path_planning/rviz/rotw2.rviz.

File -> Open Config button in RViz

File -> Open Config button in RViz

 

Opening rotw2.rviz config file in RViz

Opening rotw2.rviz config file in RViz

 

When you load the Rviz configuration file, you should get something like this:

ROS Mini Challenge #2 - RViz

ROS Mini Challenge #2 – RViz

Now, all you need to do is to set the initial localization of the robot, using the 2D Pose Estimate tool, and then set a navigation goal for the robot, using the 2D Nav Goal tool.
RViz Pose Estimate - ROS Mini Challenge

RViz Pose Estimate – ROS Mini Challenge

RViz 3D Nav Goal - ROS Mini Challenge

RViz 3D Nav Goal – ROS Mini Challenge

As you can see in the animations below, when setting the Nav Goal for the robot, you should see how it starts moving towards the goal, both in RViz and in the simulation!
ROS Mini Challenge #2 - RViz

ROS Mini Challenge #2 – RViz animation

Robot moving - ROS Mini Challenge

Robot moving simulation – ROS Mini Challenge #2

Ok, but the problem doesn’t move as expected, so… where’s the problem? If you have tried to reproduce the steps described above you have already seen that it DOES NOT WORK. When you follow the pipeline and set a navigation goal, this goal is not reached successfully. Why? Let’s figure it out in the section below.

Solving the ROS Mini Challenge

According to the instructions, we need to have a look at the my_summit_path_planning package and try to find there the errors are. If you want to try to solve it by yourself and you don’t have all the ROS Navigation knowledge, we highly recommend you trying Robot Ignite Academy.

If you already have some ROS knowledge, you will notice some errors after running the command below:

source ~/catkin_ws/devel/setup.bash
roslaunch my_summit_path_planning move_base.launch

From the error messages, the most important is:

[ INFO] [1580755905.369676908, 45.878000000]: Subscribed to Topics: hokuyo_laser
[FATAL] [1580755905.422091274, 45.888000000]: Only topics that use point clouds or laser scans are currently supported
terminate called after throwing an instance of 'std::runtime_error'
what(): Only topics that use point clouds or laser scans are currently supported

which says that only PonitCloud or LaserScan message types are supported when loading the obstacle_layer plugin, right after trying to subscribe to the hokuyo_laser topic.

Now we have to find where the plugin is defined and check the topic names and types. In the instructions, we have a hint to check the my_summit_path_planning package.

In that package, we can see that the plugin is loaded by the my_summit_path_planning/config/local_costmap_params.yaml file, but the plugin is defined in the config/costmap_common_params.yaml file:

obstacle_range: 2.5
raytrace_range: 3.0

footprint: [[0.35, -0.3], [0.35, 0.3], [-0.35,0.3], [-0.35, -0.3]]

publish_frequency: 1.0

inflation_layer:
  inflation_radius: 0.5

obstacle_layer:
  observation_sources: hokuyo_laser
  hokuyo_laser: {sensor_frame: hokuyo_base_link, data_type: Odometry, topic: /scanner, marking: true, clearing: true}

static:
  map_topic: /map
  subscribe_to_updates: true

In the configuration, we can see that the obstacle_layer laser defines hokuyo_laser,  which subscribes to the /scanner topic of type Odometry.

If we try to find that /scanner topic, the topic doesn’t exist:

rostopic list | grep scanner

Normally the topics that contain Laser data have the scan in their name. Let’s try to find a topic with that name:

$ rostopic list | grep scan
/hokuyo_base/scan

Hey, there we have. The topic name is /hokuyo_base/scan. Let’s see whether there is data being published in this topic:

rostopic echo -n1 /hokuyo_base/scan

After running the command above, we can see that data is correctly being published, so, it seems this is really the topic we are looking for. Let’s now check the topic type to see whether it has something related to laser:

$ rostopic info /hokuyo_base/scan

Type: sensor_msgs/LaserScan

Publishers:
* /gazebo (http://rosdscomputer:38225/)

Subscribers:
* /amcl (http://rosdscomputer:36673/)

There you go. The topic type is sensor_msgs/LaserScan.

Let’s then copy the topic name /hokuyo_base/scan and paste it in place of the /scanner in the my_summit_path_planning/config/costmap_common_params.yaml file. In the same file, let’s also change the Odometry data_type by LaserScan, which is the type of topic we will use.

Now, after saving the file, let’s launch our move base node with the same command used previously:

roslaunch my_summit_path_planning move_base.launch

Now you should see no errors in the output. You can know go again to RViz and correctly estimate the pose of the robot by clicking 2D Pose Estimate, and then move the robot with the 2D Nav Goal.

RViz Pose Estimate - ROS Mini Challenge

RViz Pose Estimate – ROS Mini Challenge

RViz 3D Nav Goal - ROS Mini Challenge

RViz 3D Nav Goal – ROS Mini Challenge

Now the robot should move as expected:

ROS Mini Challenge #2 - RViz

ROS Mini Challenge #2 – RViz animation

Robot moving - ROS Mini Challenge

Robot moving simulation – ROS Mini Challenge #2

 

Congratulations. You know how to solve the ROS Mini Challenge #2, in which you have to make the robot navigate to a given location.

Youtube video

So this is the post for today. Remember that we have the live version of this post on YouTube. If you liked the content, please consider subscribing to our youtube channel. We are publishing new content ~every day.

Keep pushing your ROS Learning.

Developing Web Interfaces For ROS Robots #1 – Introduction to ROSBridge Server

Developing Web Interfaces For ROS Robots #1 – Introduction to ROSBridge Server

Hello ROS Developers,

This is the 1st of 4 posts of the series Developing web interfaces for ROS Robots.

In this post, we are going to introduce the ROSBridge server and make some basic communication using a simple web page.

 

It’s based on the video series, but with improvements, to make the flow easier and more practical.


Part 1 – Creating our first webpage

First of all, we will create a folder to contain the webpage files. Through the IDE or a web shell, create the folder webpages. Inside the folder, create a file called index.html. You must have something like below:

Fill the index.html with the following content:

<html>

<head>
</head>

<body>
    <h1>Hello from ROSDS!</h1>

    <p>Communicate to rosbridge from my 1st webpage</p>
</body>

</html>

We have just created a webpage, though we need a server to provide this page for the clients (our browser, in this case). Let’s use the python “simple http server” to do that.

In a web shell, enter into the folder webpages and start the server like shown below:

user:~$ cd ~/webpages;
user:~$ python -m SimpleHTTPServer 7000;

At this point, you must have access to the webpage we have just created through your web browser. If you are on your local computer, it must be available at the address http://localhost:8000

But if you are working on ROSDS, you need to execute a command in the web shell to figure your public address:

user:~$ webpage_address
https://i-035556c2a41b8d21f.robotigniteacademy.com/webpage/

*Do not copy the output from this tutorial, each user has his/her own webpage_address!

Open the address in a new tab of the browser and must get the following page:

 

First webpage result

First webpage result


 

Part 2 – Preparing a robot to work with

In order to see everything working, we are gonna use a robot from ROSDS library. Let’s launch an empty worl with Turtlebot 2 in it. You can do it easily using the simulation menu:

 

And one more step just before coding our JavaScript files, let’s start the rosbridge server as well. Open another terminal and execute the following:

roslaunch rosbridge_server rosbridge_websocket.launch

You must have something like this as a response:

 


Part 3 – JavaScript code

Great! We have a robot and rosbridge all working together in our ROS environment. Let’s prepare our JavaScript to connect to it.

Create a new tag <script> in the <head> section of our index.html file.

<head>
	<script type="text/javascript" src="https://static.robotwebtools.org/roslibjs/current/roslib.min.js">
	</script>
</head>

This tag is including the roslibjs library. It is necessary to provide the objects that connects to rosbridge.

After that, inside the <body> tag, we create another <script> tag to have our code.

<body>
    <h1>Hello from ROSDS!</h1>

    <p>Communicate to robots from my 1st webpage</p>

    <script type="text/javascript">
      // Here it goes our code!
    </script>
</body>

And we are going to replace the comment // Here it goes our code! by the code itself!

 


Part 4 – Connecting to the rosbridge

The first part of the code is responsible to connect to rosbridge:

var ros = new ROSLIB.Ros({
    url : 'wss://i-07f610defc4f8c33c.robotigniteacademy.com/rosbridge/'
});

The value of the url attribute is different for each one. If you are working on ROSDS, get yours using the following command in a terminal:

rosbridge_address

But, if you are working on your local computer, your address will be:

ws://localhost:9090

We need also to define some callbacks in order to identify the connection to rosbridge.

ros.on('connection', function() {
    console.log('Connected to websocket server.');
});

ros.on('error', function(error) {
    console.log('Error connecting to websocket server: ', error);
});

ros.on('close', function() {
    console.log('Connection to websocket server closed.');
});

These events are triggered whenever rosbridge connection status is changed. At this point we can reload the page and observe the console of the browser. In order to debug our javascript, we need to open it. With the webpage selected, press F12. You must have the following page opened:

Note that we have the message “Connected to websocket server.” in the console. It means the connection was established successfully!


Part 5 – Sending commands to the robot

Now that we have the connection with the server, let’s send the first command, which is gonna be a Twist message to the robot.

First we define the topic object

var cmdVel = new ROSLIB.Topic({
    ros : ros,
    name : '/cmd_vel',
    messageType : 'geometry_msgs/Twist'
})

We must fill the topic name and the message type.

Then, we define the values of the message we are going to send through this topic:

var twist = new ROSLIB.Message({
    linear : {
        x : 0.5,
        y : 0.0,
        z : 0.0
    },
    angular : {
        x : 0.0,
        y : 0.0,
        z : 0.5
    }
})

Finally, we send the message through the topic:

cmdVel.publish(twist)

At this point, reload the web page and check the logs of the console.

The robot must be moving in circles:

 


ROSJect created along the post:

http://www.rosject.io/l/dd7f9b6/

[ROS Mini Challenge] #1 – Make a robot rotate according to user input

[ROS Mini Challenge] #1 – Make a robot rotate according to user input

In this post, we will see how to write a ROS program (in Python) to make a robot rotate according to user input. We are going to fix an error in the code that prevents this program from working as we go on.

PS: This ROS project is part of our ROS Mini Challenge series, which gives you an opportunity to win an amazing ROS Developers T-shirt! This challenge is already solved. For updates on future challenges, please stay tuned to our Twitter channel.

Step 1: Grab a copy of the ROS Project that contains the almost-working Python code

Click here to get your own copy of the project. If you don’t have an account on the ROS Development Studio, you will be asked to create one. Once you create an account or log in, the project will be copied to your workspace. That done, open your ROSject using the Open button. This might take a few moments, please be patient.

You should now see a notebook with detailed instructions about the challenge. Please ignore the section that says “Claim your Prize!” because, well, the challenge is closed already 🙂

Step 2: Start the Simulation and run the program

  1. Click on the Simulations menu and then Choose launch file . In the dialog that appears, select rotw1.launchthen click the Launch button. You should see a Gazebo window popup showing the simulation: a robot standing in front of a very beautiful house!
  2. Keeping the Gazebo window in view, pick a Shell from Tools > Shell and run the following commands (input the same numbers shown):
user:~$ rosrun rotw1_code rotate_robot.py
Enter desired angular speed (degrees): 60
Enter desired angle (degrees): 90
Do you want to rotate clockwise? (y/n): y
[INFO] [1580744469.184313, 1044.133000]: shutdown time! Stop the robot

Did the robot rotate? I would be surprised if it did!

Step 3: Find out what the problem is (was!)

Fire up the IDE and locate the file named rotate_robot.py. You will find in the catkin_ws/src/rotw1_code/src directory.

#!/usr/bin/env python

import rospy
from geometry_msgs.msg import Twist
from sensor_msgs.msg import LaserScan
import time


class RobotControl():

    def __init__(self):
        rospy.init_node('robot_control_node', anonymous=True)
        self.vel_publisher = rospy.Publisher('/velocity', Twist, queue_size=1)
        self.cmd = Twist()
        self.ctrl_c = False
        self.rate = rospy.Rate(10)
        rospy.on_shutdown(self.shutdownhook)

    def publish_once_in_cmd_vel(self):
        """
        This is because publishing in topics sometimes fails the first time you publish.
        In continuous publishing systems, this is no big deal, but in systems that publish only
        once, it IS very important.
        """
        while not self.ctrl_c:
            connections = self.vel_publisher.get_num_connections()
            if connections > 0:
                self.vel_publisher.publish(self.cmd)
                break
            else:
                self.rate.sleep()

    def shutdownhook(self):
        self.stop_robot()
        self.ctrl_c = True

    def stop_robot(self):
        rospy.loginfo("shutdown time! Stop the robot")
        self.cmd.linear.x = 0.0
        self.cmd.angular.z = 0.0
        self.publish_once_in_cmd_vel()

    def get_inputs_rotate(self):
        self.angular_speed_d = int(
            raw_input('Enter desired angular speed (degrees): '))
        self.angle_d = int(raw_input('Enter desired angle (degrees): '))
        clockwise_yn = raw_input('Do you want to rotate clockwise? (y/n): ')
        if clockwise_yn == "y":
            self.clockwise = True
        if clockwise_yn == "n":
            self.clockwise = False

        return [self.angular_speed_d, self.angle_d]

    def convert_degree_to_rad(self, speed_deg, angle_deg):

        self.angular_speed_r = speed_deg * 3.14 / 180
        self.angle_r = angle_deg * 3.14 / 180
        return [self.angular_speed_r, self.angle_r]

    def rotate(self):

        # Initilize velocities
        self.cmd.linear.x = 0
        self.cmd.linear.y = 0
        self.cmd.linear.z = 0
        self.cmd.angular.x = 0
        self.cmd.angular.y = 0

        # Convert speed and angle to radians
        speed_d, angle_d = self.get_inputs_rotate()
        self.convert_degree_to_rad(speed_d, angle_d)

        # Check the direction of the rotation
        if self.clockwise:
            self.cmd.angular.z = -abs(self.angular_speed_r)
        else:
            self.cmd.angular.z = abs(self.angular_speed_r)

        # t0 is the current time
        t0 = rospy.Time.now().secs

        current_angle = 0

        # loop to publish the velocity estimate, current_distance = velocity * (t1 - t0)
        while (current_angle < self.angle_r):

            # Publish the velocity
            self.vel_publisher.publish(self.cmd)
            # t1 is the current time
            t1 = rospy.Time.now().secs
            # Calculate current angle
            current_angle = self.angular_speed_r * (t1 - t0)
            self.rate.sleep()

        # set velocity to zero to stop the robot
        #self.stop_robot()


if __name__ == '__main__':
    robotcontrol_object = RobotControl()
    try:
        res = robotcontrol_object.rotate()
    except rospy.ROSInterruptException:
        pass

The problem was in the def __init__(self) function – we are publishing to the wrong topic – /velocity – instead of /cmd_vel.

Next, let’s see how we found the problem.

Step 4: Learn how the problem was solved

If you’re familiar with ROS, you’ll know that /cmd_vel is the most common name for the topic responsible for moving the robot. It’s not always that, but at least the /velocity topic instantly became suspect! Therefore, we went ahead to examine it to see if it was a proper topic:

user:~$ rostopic info /velocity
Type: geometry_msgs/Twist

Publishers:
 * /robot_control_node_5744_1580744450546 (http://rosdscomputer:45129/)

Subscribers: None

And our suspicions were confirmed: the topic had no subscribers. This was strange, as usually /gazebo would be one of the subscribers if it was connected to the simulation.

On the other hand, /cmd_vel was connected to /gazebo. So, maybe it’s the right topic after all, but let’s confirm that next.

Step 5: Replace /velocity with /cmd_vel and rotate the robot!

Replace /velocity with /cmd_vel in the def __init__(self) function and save.

Now run the program again:

user:~$ rosrun rotw1_code rotate_robot.py
Enter desired angular speed (degrees): 60
Enter desired angle (degrees): 90
Do you want to rotate clockwise? (y/n): y
[INFO] [1580744469.184313, 1044.133000]: shutdown time! Stop the robot

Now, the robot should rotate according to user input – try different numbers!

And that was it. Hope you found this useful.

Extra: Video of this post

We made a video showing how this challenge was solved. If you prefer “sights and sounds” to “black and white”, here you go:

Related Resources

Feedback

Did you like this post? Do you have questions about what was explained? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS or ROS2 topics, please let us know in the comments area and we will do a video or post about it.

Pin It on Pinterest