Welcome to the ROS Q&A series again. In this post, we are going to answer and solve a question from a user on Gazebo Answers which asks how to launch a Parrot Drone simulation locally. The question can be found here.
What will you learn in this post
How to spawn a drone simulation in your local computer
Before downloading the repositories, let’s first create a catkin workspace for the parrot drone, which we will call parrot_ws. Let’s do that with the commands below:
mkdir -p ~/parrot_ws/src
cd ~/parrot_ws/
Now we have the parrot_ws (parrot workspace). Let’s then download the required git repositories which contain the simulation:
cd ~/parrot_ws/src
git clone --branch kinetic-gazebo7 https://bitbucket.org/theconstructcore/parrot_ardrone.git
git clone https://bitbucket.org/theconstructcore/spawn_robot_tools.git
You should now have the folders parrot_ardrone and spawn_robot_tools on the ~/parrot_ws/src folder.
Installing ignition-math, used by sjtu_drone
Before compiling the packages we downloaded in the previous step, we will need to install the Ignition Math library, which is used by the sjtu_drone drone found on the parrot_ardrone repository we cloned previously.
We can install the Ignition Math library with the following commands:
Now that we have the Ignition Math library installed, we can proceed to compile the packages in our workspace.
Compiling the drone package
Now that we have everything in place, we can compile our packages we downloaded previously.
IMPORTANT: it may happen that when compiling the package, catkin_make can’t find the ignition library, so we need to export the CXXFLAGS accordingly. In my local computer, the ignition library is found at /usr/include/ignition/math4, so to compile we use the following commands:
cd ~/parrot_ws
export CXXFLAGS=-isystem\ /usr/include/ignition/math4
source /opt/ros/kinetic/setup.bash
catkin_make
If everything worked as expected, great. If not, try removing the devel and build first with cd ~/parrot_ws/; rm -rf build/ devel/ and compile it again with the commands exemplified above.
If everything went ok, you should have something like:
root@79e5b2505ede:~/parrot_ws# export CXXFLAGS=-isystem\ /usr/include/ignition/math4
root@79e5b2505ede:~/parrot_ws# source /opt/ros/kinetic/setup.bash; catkin_make
Base path: /root/parrot_ws
Source space: /root/parrot_ws/src
Build space: /root/parrot_ws/build
Devel space: /root/parrot_ws/devel
Install space: /root/parrot_ws/install
####
#### Running command: "cmake /root/parrot_ws/src -DCATKIN_DEVEL_PREFIX=/root/parrot_ws/devel -DCMAKE_INSTALL_PREFIX=/root/parrot_ws/install -G Unix Makefiles" in "/root/parrot_ws/build"
####
...
-- catkin 0.7.14
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~ traversing 6 packages in topological order:
-- ~~ - custom_teleop
-- ~~ - drone_demo
-- ~~ - ardrone_as
-- ~~ - sjtu_drone
-- ~~ - spawn_robot_tools_pkg
-- ~~ - drone_construct
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ processing catkin package: 'custom_teleop'
-- ==> add_subdirectory(parrot_ardrone/custom_teleop)
-- +++ processing catkin package: 'drone_demo'
-- ==> add_subdirectory(parrot_ardrone/drone_demo)
-- +++ processing catkin package: 'ardrone_as'
-- ==> add_subdirectory(parrot_ardrone/ardrone_as)
-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
-- Generating .msg files for action ardrone_as/Ardrone /root/parrot_ws/src/parrot_ardrone/ardrone_as/action/Ardrone.action
Generating for action Ardrone
-- ardrone_as: 7 messages, 0 services
-- +++ processing catkin package: 'sjtu_drone'
-- ==> add_subdirectory(parrot_ardrone/sjtu_drone)
-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
-- Boost version: 1.58.0
-- Found the following Boost libraries:
-- thread
-- signals
-- system
-- filesystem
-- program_options
-- regex
-- iostreams
-- date_time
-- chrono
-- atomic
-- Found Protobuf: /usr/lib/x86_64-linux-gnu/libprotobuf.so
...
-- Generating done
-- Build files have been written to: /root/parrot_ws/build
####
#### Running command: "make -j4 -l4" in "/root/parrot_ws/build"
####
Scanning dependencies of target _ardrone_as_generate_messages_check_deps_ArdroneActionFeedback
Scanning dependencies of target _ardrone_as_generate_messages_check_deps_ArdroneGoal
Scanning dependencies of target _ardrone_as_generate_messages_check_deps_ArdroneActionGoal
Scanning dependencies of target _ardrone_as_generate_messages_check_deps_ArdroneResult
[ 0%] Built target _ardrone_as_generate_messages_check_deps_ArdroneResult
...
Scanning dependencies of target plugin_drone
[ 1%] Building CXX object parrot_ardrone/sjtu_drone/CMakeFiles/plugin_ros_cam.dir/src/plugin_ros_cam.cpp.o
[ 3%] Building CXX object parrot_ardrone/sjtu_drone/CMakeFiles/plugin_ros_imu.dir/src/plugin_ros_imu_native.cpp.o
[ 5%] Building CXX object parrot_ardrone/sjtu_drone/CMakeFiles/plugin_drone.dir/src/plugin_drone.cpp.o
[ 7%] Building CXX object parrot_ardrone/sjtu_drone/CMakeFiles/plugin_ros_sonar.dir/src/plugin_ros_sonar.cpp.o
...
[ 9%] Linking CXX shared library /root/parrot_ws/src/parrot_ardrone/sjtu_drone/plugins/libplugin_ros_imu.so
[ 11%] Linking CXX shared library /root/parrot_ws/src/parrot_ardrone/sjtu_drone/plugins/libplugin_ros_sonar.so
[ 11%] Built target plugin_ros_sonar
[ 13%] Building CXX object parrot_ardrone/sjtu_drone/CMakeFiles/plugin_ros_cam.dir/src/util_ros_cam.cpp.o
[ 13%] Built target plugin_ros_imu
Scanning dependencies of target plugin_ros_init
[ 15%] Building CXX object parrot_ardrone/sjtu_drone/CMakeFiles/plugin_ros_init.dir/src/plugin_ros_init.cpp.o
[ 17%] Building CXX object parrot_ardrone/sjtu_drone/CMakeFiles/plugin_drone.dir/src/pid_controller.cpp.o
[ 19%] Linking CXX shared library /root/parrot_ws/src/parrot_ardrone/sjtu_drone/plugins/libplugin_drone.so
[ 19%] Built target plugin_drone
Scanning dependencies of target spawn_drone
[ 21%] Building CXX object parrot_ardrone/sjtu_drone/CMakeFiles/spawn_drone.dir/src/spawn_drone.cpp.o
...
[ 23%] Linking CXX executable /root/parrot_ws/src/parrot_ardrone/sjtu_drone/bin/spawn_drone
[ 23%] Built target spawn_drone
Scanning dependencies of target spawn_robot_tools_pkg_xacro_generated_to_devel_space_
[ 23%] Built target spawn_robot_tools_pkg_xacro_generated_to_devel_space_
Scanning dependencies of target ardrone_as_generate_messages_cpp
[ 25%] Generating C++ code from ardrone_as/ArdroneFeedback.msg
[ 27%] Generating C++ code from ardrone_as/ArdroneActionResult.msg
[ 29%] Generating C++ code from ardrone_as/ArdroneAction.msg
[ 31%] Generating C++ code from ardrone_as/ArdroneResult.msg
[ 33%] Generating C++ code from ardrone_as/ArdroneActionGoal.msg
[ 35%] Generating C++ code from ardrone_as/ArdroneGoal.msg
[ 37%] Generating C++ code from ardrone_as/ArdroneActionFeedback.msg
Scanning dependencies of target ardrone_as_generate_messages_py
[ 37%] Built target ardrone_as_generate_messages_cpp
[ 39%] Generating Python from MSG ardrone_as/ArdroneFeedback
Scanning dependencies of target ardrone_as_generate_messages_nodejs
[ 41%] Generating Javascript code from ardrone_as/ArdroneFeedback.msg
[ 43%] Generating Javascript code from ardrone_as/ArdroneActionResult.msg
[ 47%] Generating Javascript code from ardrone_as/ArdroneAction.msg
[ 47%] Generating Python from MSG ardrone_as/ArdroneActionResult
[ 49%] Generating Javascript code from ardrone_as/ArdroneResult.msg
[ 50%] Generating Javascript code from ardrone_as/ArdroneActionGoal.msg
[ 52%] Generating Python from MSG ardrone_as/ArdroneAction
[ 54%] Generating Javascript code from ardrone_as/ArdroneGoal.msg
[ 56%] Generating Javascript code from ardrone_as/ArdroneActionFeedback.msg
[ 58%] Generating Python from MSG ardrone_as/ArdroneResult
[ 58%] Built target ardrone_as_generate_messages_nodejs
Scanning dependencies of target ardrone_as_generate_messages_eus
[ 60%] Generating EusLisp code from ardrone_as/ArdroneFeedback.msg
[ 62%] Generating EusLisp code from ardrone_as/ArdroneActionResult.msg
[ 64%] Generating Python from MSG ardrone_as/ArdroneActionGoal
[ 66%] Generating EusLisp code from ardrone_as/ArdroneAction.msg
[ 68%] Generating Python from MSG ardrone_as/ArdroneGoal
[ 70%] Generating EusLisp code from ardrone_as/ArdroneResult.msg
[ 72%] Generating EusLisp code from ardrone_as/ArdroneActionGoal.msg
[ 74%] Generating Python from MSG ardrone_as/ArdroneActionFeedback
[ 76%] Generating EusLisp code from ardrone_as/ArdroneGoal.msg
[ 78%] Generating EusLisp code from ardrone_as/ArdroneActionFeedback.msg
[ 80%] Generating Python msg __init__.py for ardrone_as
[ 82%] Generating EusLisp manifest code for ardrone_as
[ 82%] Built target ardrone_as_generate_messages_py
Scanning dependencies of target ardrone_as_generate_messages_lisp
[ 84%] Generating Lisp code from ardrone_as/ArdroneFeedback.msg
[ 86%] Generating Lisp code from ardrone_as/ArdroneActionResult.msg
[ 88%] Generating Lisp code from ardrone_as/ArdroneAction.msg
[ 90%] Generating Lisp code from ardrone_as/ArdroneResult.msg
[ 92%] Generating Lisp code from ardrone_as/ArdroneActionGoal.msg
[ 94%] Generating Lisp code from ardrone_as/ArdroneGoal.msg
[ 96%] Generating Lisp code from ardrone_as/ArdroneActionFeedback.msg
[ 96%] Built target ardrone_as_generate_messages_lisp
[ 96%] Built target ardrone_as_generate_messages_eus
Scanning dependencies of target ardrone_as_generate_messages
[ 96%] Built target ardrone_as_generate_messages
...
[ 98%] Linking CXX shared library /root/parrot_ws/src/parrot_ardrone/sjtu_drone/plugins/libplugin_ros_cam.so
[ 98%] Built target plugin_ros_cam
[100%] Linking CXX shared library /root/parrot_ws/src/parrot_ardrone/sjtu_drone/plugins/libplugin_ros_init.so
[100%] Built target plugin_ros_init
Once the package is compiled, in every shell you need to source the parrot_ws so that ROS can find the compiled packages. In each shell you can run the commands below:
cd ~/parrot_ws/
source devel/setup.bash
rospack profile
Launching the simulation
Now that you have everything in place, you should be able to launch the simulation with the command below:
After running rosrun teleop_twist_keyboard teleop_twist_keyboard.py you can easily move the robot using the keyboard.
So that is the post for today. You can also find a live version of this post on our YouTube channel in the link below. Also, please consider subscribing to our channel if you liked the content and press the bell for a new video every day.
The first thing you need to do is to have a copy of the ROSject we mentioned above. Click on the link to get an automatic copy. You should now see a ROSject called BuoyancyBot on your list of ROSjects, something like the image below:
BuoyancyBot simulation on ROSDS
After clicking on the Open button to open the ROSject, you should have the environment like the one in the image below:
BuoyancyBot opened on ROSDS
Finding the code
After opening the ROSject, you can see the code on the ~/simulation_ws/src folder. There you will find the folders buoyancy_tests_pkg and spawn_robot_tools. It is worth mentioning that the plugin we created is called fluid_resitance.cpp, which has the content below. Bear in mind that you can find that code on the ROSject:
#include "ros/callback_queue.h"
#include "ros/ros.h"
#include "ros/subscribe_options.h"
#include "std_msgs/Float32.h"
#include <functional>
#include <gazebo/common/common.hh>
#include <gazebo/gazebo.hh>
#include <gazebo/msgs/msgs.hh>
#include <gazebo/physics/physics.hh>
#include <gazebo/transport/transport.hh>
#include <ignition/math/Vector3.hh>
#include <thread>
namespace gazebo {
class FluidResitance : public ModelPlugin {
public:
void Load(physics::ModelPtr _parent, sdf::ElementPtr _sdf) {
if (_sdf->HasElement("fluid_resitanceTopicName")) {
this->fluid_resitanceTopicName =
_sdf->Get<std::string>("fluid_resitanceTopicName");
} else {
ROS_WARN("No Topic Given name given, setting default name %s",
this->fluid_resitanceTopicName.c_str());
}
if (_sdf->HasElement("NameLinkToApplyResitance")) {
this->NameLinkToApplyResitance =
_sdf->Get<std::string>("NameLinkToApplyResitance");
} else {
ROS_WARN("No NameLinkToApplyResitance Given name given, setting default "
"name %s",
this->NameLinkToApplyResitance.c_str());
}
if (_sdf->HasElement("rate")) {
this->rate = _sdf->Get<double>("rate");
} else {
ROS_WARN("No rate Given name given, setting default "
"name %f",
this->rate);
}
// Store the pointer to the model
this->model = _parent;
this->world = this->model->GetWorld();
this->link_to_apply_resitance =
this->model->GetLink(this->NameLinkToApplyResitance);
// Listen to the update event. This event is broadcast every
// simulation iteration.
this->updateConnection = event::Events::ConnectWorldUpdateBegin(
std::bind(&FluidResitance::OnUpdate, this));
// Create a topic name
// std::string fluid_resitance_index_topicName = "/fluid_resitance_index";
// Initialize ros, if it has not already bee initialized.
if (!ros::isInitialized()) {
int argc = 0;
char **argv = NULL;
ros::init(argc, argv, "model_mas_controler_rosnode",
ros::init_options::NoSigintHandler);
}
// Create our ROS node. This acts in a similar manner to
// the Gazebo node
this->rosNode.reset(new ros::NodeHandle("model_mas_controler_rosnode"));
#if (GAZEBO_MAJOR_VERSION >= 8)
this->last_time = this->world->SimTime().Float();
#else
this->last_time = this->world->GetSimTime().Float();
#endif
// Freq
ros::SubscribeOptions so = ros::SubscribeOptions::create<std_msgs::Float32>(
this->fluid_resitanceTopicName, 1,
boost::bind(&FluidResitance::OnRosMsg, this, _1), ros::VoidPtr(),
&this->rosQueue);
this->rosSub = this->rosNode->subscribe(so);
// Spin up the queue helper thread.
this->rosQueueThread =
std::thread(std::bind(&FluidResitance::QueueThread, this));
ROS_WARN("Loaded FluidResitance Plugin with parent...%s, With Fluid "
"Resitance = %f "
"Started ",
this->model->GetName().c_str(), this->fluid_resitance_index);
}
// Called by the world update start event
public:
void OnUpdate() {
float period = 1.0 / this->rate;
// Get simulator time
#if (GAZEBO_MAJOR_VERSION >= 8)
float current_time = this->world->SimTime().Float();
#else
float current_time = this->world->GetSimTime().Float();
#endif
float dt = current_time - this->last_time;
if (dt <= period){
ROS_DEBUG(">>>>>>>>>>TimePassed = %f, TimePeriod =%f ",dt, period);
return;
}else{
this->last_time = current_time;
this->ApplyResitance();
}
}
public:
void SetResitance(const double &_force) {
this->fluid_resitance_index = _force;
ROS_WARN("model_fluid_resitance_index changed >> %f",
this->fluid_resitance_index);
}
void UpdateLinearVel() {
#if (GAZEBO_MAJOR_VERSION >= 8)
this->now_lin_vel = this->model->RelativeLinearVel();
#else
this->now_lin_vel = this->model->GetRelativeLinearVel();
#endif
}
void ApplyResitance() {
this->UpdateLinearVel();
#if (GAZEBO_MAJOR_VERSION >= 8)
ignition::math::Vector3d force, torque;
#else
math::Vector3 force, torque;
#endif
ROS_WARN("LinearSpeed = [%f,%f,%f] ",this->now_lin_vel.x, this->now_lin_vel.y, this->now_lin_vel.z);
force.x = -1.0 * this->fluid_resitance_index * this->now_lin_vel.x;
force.y = -1.0 * this->fluid_resitance_index * this->now_lin_vel.y;
force.z = -1.0 * this->fluid_resitance_index * this->now_lin_vel.z;
// Changing the mass
this->link_to_apply_resitance->AddRelativeForce(force);
#if (GAZEBO_MAJOR_VERSION >= 8)
this->link_to_apply_resitance->AddRelativeTorque(
torque -
this->link_to_apply_resitance->GetInertial()->CoG().Cross(force));
#else
this->link_to_apply_resitance->AddRelativeTorque(
torque -
this->link_to_apply_resitance->GetInertial()->GetCoG().Cross(force));
#endif
ROS_WARN("FluidResitanceApplying = [%f,%f,%f] ",force.x, force.y, force.z);
}
public:
void OnRosMsg(const std_msgs::Float32ConstPtr &_msg) {
this->SetResitance(_msg->data);
}
/// \brief ROS helper function that processes messages
private:
void QueueThread() {
static const double timeout = 0.01;
while (this->rosNode->ok()) {
this->rosQueue.callAvailable(ros::WallDuration(timeout));
}
}
// Pointer to the model
private:
physics::ModelPtr model;
// Pointer to the update event connection
private:
event::ConnectionPtr updateConnection;
// Mas of model
double fluid_resitance_index = 1.0;
/// \brief A node use for ROS transport
private:
std::unique_ptr<ros::NodeHandle> rosNode;
/// \brief A ROS subscriber
private:
ros::Subscriber rosSub;
/// \brief A ROS callbackqueue that helps process messages
private:
ros::CallbackQueue rosQueue;
/// \brief A thread the keeps running the rosQueue
private:
std::thread rosQueueThread;
/// \brief A ROS subscriber
private:
physics::LinkPtr link_to_apply_resitance;
private:
std::string fluid_resitanceTopicName = "fluid_resitance";
private:
std::string NameLinkToApplyResitance = "base_link";
private:
#if (GAZEBO_MAJOR_VERSION >= 8)
ignition::math::Vector3d now_lin_vel;
#else
math::Vector3 now_lin_vel;
#endif
private:
double rate = 1.0;
private:
float last_time = 0.0;
private:
/// \brief The parent World
physics::WorldPtr world;
};
// Register this plugin with the simulator
GZ_REGISTER_MODEL_PLUGIN(FluidResitance)
} // namespace gazebo
Compile the plugin
Once you have the ROSject, be it on ROSDS or on your own computer, you can easily compile it with:
cd ~/simulation_ws/
catkin_make
Everything should have compiled without any problems. If compiling in your own computer raises errors, please remember running the command source /opt/ros/kinetic/setup.bash before calling catkin_make .
Using the plugin
The plugin we compiled previously has to be loaded by gazebo. We do that on the file
~/simulation_ws/src/buoyancy_tests_pkg/buoyancy_tests_pkg/urdf/simple_floating_sphere_buoyancy_control.urdf, by using the instructions below:
With everything in place, we can now run the simulation. To launch the Simple Fluid Machine, we launch the simulation that has the mass control, the fluid resistance and the propulsion plugins enabled:
If you have applied a movement and you change the FluidResistance, you should see a variation in the time the robot takes to stop. The higher the friction, the faster it will stop its movement.
Air Dragging in Gazebo on ROSDS
There is more to it but have a look at the HectorPluginsAerodinamics, in which this code is based upon.
Change the Mass or buoyancy
By changing the mass, it will change the buoyancy of the model. This is to simulate the water intake of submarines or the buoyancy system that an air flying machine like a blimp could have.
rostopicpub/model_massstd_msgs/Float32"data: 0.0"
Mass_0 = 7.23822947387 –> Neutral buoyancy because its the mass of the object
Mass > Mass_0 –> It will sink
Mass < Mass_0 –> It will rise
This mass you can find it in the URDF of the model: buoyancy_tests_pkg/urdf/simple_floating_sphere_buoyancy_control.urdf
So, that is the post for today. Remember that we also have a live version of this post on our YouTube channel, as can be seen on the link below. If you liked the content of this post or video, please consider subscribing to our channel and pressing the bell for a new video every single day:
In this post, you will learn how to create a simple ROS2 package for Python. You don’t need a ROS2 installation for this as we will use the ROS Development Studio (ROSDS), an online platform that provides access to ROS (1 or 2) computers and other powerful ROS tools within a browser!
PS: If you have ROS2 installed locally on your machine, you can skip Step 1.
Head to http://rosds.online and create a project with a similar configuration as the one shown below. You can change the details as you like, but please ensure you select “Ubuntu 18.04 + ROS2 Crystal” under “Configuration”.
Once done with that, open your ROSject. This might take a few moments, please be patient.
Step 2: Source the ROS2 workspace
Once the ROSject is open, head to the Tools menu and pick the Shell tool (if on your local PC just fire up a terminal) and run the following command to source the workspace:
user:~$ source /opt/ros/crystal/setup.bash
ROS_DISTRO was set to 'melodic' before. Please make sure that the environment does not mix paths from different distributions.
user:~$
If you get that ROS_DISTRO warning, just ignore it.
Step 3: Create a ROS2 Python package in your ROS2 workspace
The syntax for creating a ROS2 Python package is ros2 pkg create <package_name>.
In the same terminal as in Step 2, change to your ROS2 workspace src directory and create a package there:
user:~$ cd ros2_ws/src
user:~/ros2_ws/src$ ros2 pkg create ros2_demo_py
going to create a new package
package name: ros2_demo_py
destination directory: /home/user/ros2_ws/src
package format: 2
version: 0.0.0
description: TODO: Package description
maintainer: ['user <user@todo.todo>']
licenses: ['TODO: License declaration']
build type: ament_cmake
dependencies: []
creating folder ./ros2_demo_py
creating ./ros2_demo_py/package.xml
creating source and include folder
creating folder ./ros2_demo_py/src
creating folder ./ros2_demo_py/include/ros2_demo_py
creating ./ros2_demo_py/CMakeLists.txt
user:~/ros2_ws/src$
Step 4: Delete CMakeLists.txt , create setup.py and setup.cfg and edit package.xml
Unlike ROS1, ROS2 Python packages don’t use CMakeList.txt, but a new setup.py file. Let’s create one here. In the Shell:
Locate package.xml and replace the contents with this snippet:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>ros2_demo_py</name>
<version>0.7.3</version>
<description>A simple ROS2 Python package</description>
<maintainer email="sloretz@openrobotics.org">Shane Loretz</maintainer>
<license>Apache License 2.0</license>
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<!-- These test dependencies are optional
Their purpose is to make sure that the code passes the linters -->
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
We created the __init__.py file so that the ros2_demo_py folder could be recognized as a python module directory.
Locate the demo.py file, open it in the IDE and paste in the following code:
# Copyright 2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic')
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
def timer_callback(self):
msg = String()
msg.data = 'Hello World: %d' % self.i
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
rclpy.spin(minimal_publisher)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Step 6: Compile the package and source the workspace
We have created all the files needed. Now let’s compile.
Did you like this post? Do you have questions about what is 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 topics, please let us know in the comments area and we will do a video or post about it
You can open it by clicking on the Open button or you can download it by clicking on the download button, the ones pointed below by the red and green arrows respectively.
The ROSJect mentioned here basically contains the following git repositories on it:
When you open a ROSject, by default you have the Jupyter Notebook automatically open, but if that doesn’t happen, you can manually open it by clicking on Tools -> Jupyter Notebook as shown in the image below:
Now on the Jupyter Notebook window let’s click openmanipulator_morpheus_chair_notebooks, then click on Notes_Commands.ipynb to open it. That notebook contains the instructions we are going to follow in this post.
Start Demo Pick and place Everything:
In order to start the demo, let’s first run the Service Server that will be used to move the arm: