How to create a ROS Sensor Plugin for Gazebo

how-to-create-a-ros-sensor-plugin-for-gazebo

Written by Ricardo Tellez

20/05/2015

 

There are magnificent tutorials about how to create plugins for Gazebo in the GazeboSim webpage. There are even some tutorials about how to create plugins for Gazebo + ROS. Those tutorials show that there are several types of plugins (world, model, sensor, system, visual), and indicate how to create a plugin for a world type plugin.

Recently I need to create a plugin for a light detector. Reading the tutorials, I missed a concrete example about how to create a sensor plugin. Hence, I had to investigate a little bit about it. The result is the content of this post.

 

How to: light sensor plugin in Gazebo

Following the indications provided at the answers forum of Gazebo, I decided to build a very simple light detector sensor based on a camera. Instead of using a raytracing algorithm from lights, the idea is to use a camera to capture an image, then use the image to calculate the illuminance of the image, and then publish that illuminance value through a ROS topic.

Since the plugin is for its use with ROS, the whole plugin should be compilable using a ROS environment. Hence, be sure that you have installed the following packages in your Linux system:

  • ros-<your_ros_version>-<your_gazebo_version>-ros. (in my case it is ros-jade-gazebo6-ros)
  • ros-<your_ros_version>-<your_gazebo_version>-plugins (in my case it is ros-jade-gazebo6-plugins)

This tutorial, has two parts: on the first one we will explain how to create the plugin, and on the second, how to test that it works

 

Creating the plugin

Creating a ROS package for the plugin

First thing, is to create the package in our catkin workspace that will allow us to compile the plugin without a problem.

cd ~/catkin_ws/src
catkin_create_pkg gazebo_light_sensor_plugin gazebo_ros gazebo_plugins roscpp

Creating the plugin code

For this purpose, since we are using a camera to capture the light, we are going to create a plugin class that inherits from the CameraPlugin. The code that follows has been created taking as guideline the code of the authentic gazebo ROS camera plugin.

Create a file called light_sensor_plugin.h inside the include directory of your package, including the following code:

#ifndef GAZEBO_ROS_LIGHT_SENSOR_HH
#define GAZEBO_ROS_LIGHT_SENSOR_HH

#include <string>

// library for processing camera data for gazebo / ros conversions
#include <gazebo/plugins/CameraPlugin.hh>

#include <gazebo_plugins/gazebo_ros_camera_utils.h>

namespace gazebo
{
  class GazeboRosLight : public CameraPlugin, GazeboRosCameraUtils
  {
    /// \brief Constructor
    /// \param parent The parent entity, must be a Model or a Sensor
    public: GazeboRosLight();

    /// \brief Destructor
    public: ~GazeboRosLight();

    /// \brief Load the plugin
    /// \param take in SDF root element
    public: void Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf);

    /// \brief Update the controller
    protected: virtual void OnNewFrame(const unsigned char *_image,
    unsigned int _width, unsigned int _height,
    unsigned int _depth, const std::string &_format);

    ros::NodeHandle _nh;
    ros::Publisher _sensorPublisher;

    double _fov;
    double _range;
  };
}
#endif

As you can see, the code includes a node handler to connect to the roscore. It also defines a publisher that will publish messages containing the illuminance value. Two parameters have been defined: fov (field of view) and range. At present only fov is used to indicate the amount of pixels around the center of the image that will be taken into account to calculate the illuminance.

Next step is to create a file named light_sensor_plugin.cpp containing the following code in the src directory of your package:

#include <gazebo/common/Plugin.hh>
#include <ros/ros.h>
#include "gazebo_light_sensor_plugin/light_sensor_plugin.h"

#include "gazebo_plugins/gazebo_ros_camera.h"

#include <string>

#include <gazebo/sensors/Sensor.hh>
#include <gazebo/sensors/CameraSensor.hh>
#include <gazebo/sensors/SensorTypes.hh>

#include <sensor_msgs/Illuminance.h>

namespace gazebo
{
  // Register this plugin with the simulator
  GZ_REGISTER_SENSOR_PLUGIN(GazeboRosLight)

  ////////////////////////////////////////////////////////////////////////////////
  // Constructor
  GazeboRosLight::GazeboRosLight():
  _nh("light_sensor_plugin"),
  _fov(6),
  _range(10)
  {
    _sensorPublisher = _nh.advertise<sensor_msgs::Illuminance>("lightSensor", 1);
  }

  ////////////////////////////////////////////////////////////////////////////////
  // Destructor
  GazeboRosLight::~GazeboRosLight()
  {
    ROS_DEBUG_STREAM_NAMED("camera","Unloaded");
  }

  void GazeboRosLight::Load(sensors::SensorPtr _parent, sdf::ElementPtr _sdf)
  {
    // Make sure the ROS node for Gazebo has already been initialized
    if (!ros::isInitialized())
    {
      ROS_FATAL_STREAM("A ROS node for Gazebo has not been initialized, unable to load plugin. "
        << "Load the Gazebo system plugin 'libgazebo_ros_api_plugin.so' in the gazebo_ros package)");
      return;
    }

    CameraPlugin::Load(_parent, _sdf);
    // copying from CameraPlugin into GazeboRosCameraUtils
    this->parentSensor_ = this->parentSensor;
    this->width_ = this->width;
    this->height_ = this->height;
    this->depth_ = this->depth;
    this->format_ = this->format;
    this->camera_ = this->camera;

    GazeboRosCameraUtils::Load(_parent, _sdf);
  }

  ////////////////////////////////////////////////////////////////////////////////
  // Update the controller
  void GazeboRosLight::OnNewFrame(const unsigned char *_image,
    unsigned int _width, unsigned int _height, unsigned int _depth,
    const std::string &_format)
  {
    static int seq=0;

    this->sensor_update_time_ = this->parentSensor_->GetLastUpdateTime();

    if (!this->parentSensor->IsActive())
    {
      if ((*this->image_connect_count_) > 0)
      // do this first so there's chance for sensor to run once after activated
        this->parentSensor->SetActive(true);
    }
    else
    {
      if ((*this->image_connect_count_) > 0)
      {
        common::Time cur_time = this->world_->GetSimTime();
        if (cur_time - this->last_update_time_ >= this->update_period_)
        {
          this->PutCameraData(_image);
          this->PublishCameraInfo();
          this->last_update_time_ = cur_time;

          sensor_msgs::Illuminance msg;
          msg.header.stamp = ros::Time::now();
          msg.header.frame_id = "";
          msg.header.seq = seq;

          int startingPix = _width * ( (int)(_height/2) - (int)( _fov/2)) - (int)(_fov/2);

          double illum = 0;
          for (int i=0; i<_fov ; ++i)
          {
            int index = startingPix + i*_width;
            for (int j=0; j<_fov ; ++j)
              illum += _image[index+j];
          }

          msg.illuminance = illum/(_fov*_fov);
          msg.variance = 0.0;

          _sensorPublisher.publish(msg);

          seq++;
        }
      }
    }
  }
}

That is the code that calculates the illuminance in a very simple way. Basically, it just adds the values of all the pixels in the fov of the camera and then divides by the total number of pixels.

Create a proper CMakeLists.txt

Substitute the code of the automatically created CMakeLists.txt by the code below:

cmake_minimum_required(VERSION 2.8.3)
project(gazebo_light_sensor_plugin)

find_package(catkin REQUIRED COMPONENTS
  gazebo_plugins
  gazebo_ros
  roscpp
)

find_package (gazebo REQUIRED)

catkin_package(
  INCLUDE_DIRS include
  CATKIN_DEPENDS gazebo_plugins gazebo_ros roscpp
)

###########
## Build ##
###########

set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")

link_directories(${GAZEBO_LIBRARY_DIRS})
include_directories(include)
include_directories( ${catkin_INCLUDE_DIRS} 
                     ${Boost_INCLUDE_DIR} 
                     ${GAZEBO_INCLUDE_DIRS}
)

add_library(${PROJECT_NAME} src/light_sensor_plugin.cpp)

## Specify libraries to link a library or executable target against
target_link_libraries( ${PROJECT_NAME} ${catkin_LIBRARIES} ${GAZEBO_LIBRARIES} CameraPlugin )

Update the package.xml and compile

Now you need to include the following line in your package.xml, between the tags <export></export>

<gazebo_ros plugin_path="${prefix}/lib" gazebo_media_path="${prefix}" />

Now you are ready to compile the plugin. Compilation should generate the library containing the plugin inside your building directory.

> roscd
> cd ..
> catkin_make

Testing the Plugin

Let’s create a world file containing the plugin and launch it to see how it works

Create a world file

You need a world file that includes the plugin. Here it is an example. Create a worlds directory inside your plugin package, and save the following code in a file entitled light.world. This world file just loads the camera with its plugin so it may be a bit ugly but enough for your tests. Feel free to add more elements and models in the world file (like for example, in the picture at the top of this post).

<?xml version="1.0" ?>
<sdf version="1.4">
 <world name="default">
 <include>
   <uri>model://ground_plane</uri>
 </include>

 <include>
   <uri>model://sun</uri>
 </include>

 <!-- reference to your plugin -->
 <model name='camera'>
   <pose>0 -1 0.05 0 -0 0</pose>
   <link name='link'>
     <inertial>
       <mass>0.1</mass>
       <inertia>
         <ixx>1</ixx>
         <ixy>0</ixy>
         <ixz>0</ixz>
         <iyy>1</iyy>
         <iyz>0</iyz>
         <izz>1</izz>
       </inertia>
     </inertial>
     <collision name='collision'>
       <geometry>
         <box>
            <size>0.1 0.1 0.1</size>
         </box>
       </geometry>
       <max_contacts>10</max_contacts>
       <surface>
         <contact>
           <ode/>
         </contact>
         <bounce/>
         <friction>
           <ode/>
         </friction>
       </surface>
     </collision>
     <visual name='visual'>
       <geometry>
         <box>
           <size>0.1 0.1 0.1</size>
         </box>
       </geometry>
     </visual>
     <sensor name='camera' type='camera'>
       <camera name='__default__'>
         <horizontal_fov>1.047</horizontal_fov>
         <image>
           <width>320</width>
           <height>240</height>
         </image>
         <clip>
           <near>0.1</near>
           <far>100</far>
         </clip>
       </camera>
       <plugin name="gazebo_light_sensor_plugin" filename="libgazebo_light_sensor_plugin.so">
         <cameraName>camera</cameraName>
         <alwaysOn>true</alwaysOn>
         <updateRate>10</updateRate>
         <imageTopicName>rgb/image_raw</imageTopicName>
         <depthImageTopicName>depth/image_raw</depthImageTopicName>
         <pointCloudTopicName>depth/points</pointCloudTopicName>
         <cameraInfoTopicName>rgb/camera_info</cameraInfoTopicName>
         <depthImageCameraInfoTopicName>depth/camera_info</depthImageCameraInfoTopicName>
         <frameName>camera_depth_optical_frame</frameName>
         <baseline>0.1</baseline>
         <distortion_k1>0.0</distortion_k1>
         <distortion_k2>0.0</distortion_k2>
         <distortion_k3>0.0</distortion_k3>
         <distortion_t1>0.0</distortion_t1>
         <distortion_t2>0.0</distortion_t2>
         <pointCloudCutoff>0.4</pointCloudCutoff>
         <robotNamespace>/</robotNamespace>
       </plugin>
     </sensor>
     <self_collide>0</self_collide>
     <kinematic>0</kinematic>
     <gravity>1</gravity>
   </link>
 </model>
 </world>
</sdf>

 

Create a launch file

Now the final step, to create a launch that will upload everything for you. Save the following code as main.launch inside the launch directory of you package.

<launch>
  <!-- We resume the logic in empty_world.launch, changing only the name of the world to be launched -->
  <include file="$(find gazebo_ros)/launch/empty_world.launch">
    <arg name="verbose" value="true"/>
    <arg name="world_name" value="$(find gazebo_light_sensor_plugin)/worlds/light.world"/>
    <!-- more default parameters can be changed here -->
  </include>
</launch>

 

Ready to run!

Now launch the world. Be sure that a roscore is running or your machine, and that the GAZEBO_PLUGIN_PATH environment var includes the path to the new plugin.

Now execute the following command:

roslaunch gazebo_light_sensor_plugin main.launch

You can see what the camera is observing by running the following command:

rosrun image_view image_view image:=/camera/rgb/image_raw

After running that command, a small window will appear in your screen showing what the camera is capturing. Of course, since your world is completely empty, you will only see something like as ugly as this:

Screenshot from 2016-02-11 13:07:19

Try to add some stuff in front of the camera and see how it is actually working.

Screenshot from 2015-05-20 17:34:57

Now it is time to check the value of illuminance by watching the published topic (/light_sensor_plugin/lightSensor). Just type the following and you are done:

rostopic echo /light_sensor_plugin/lightSensor

You should see the topic messages been published in your screen, something like this:

Screenshot from 2015-05-20 17:35:32

 

Conclusion

Now you have a plugin for your Gazebo simulations that can measure (very roughly) the light detected. It can be improved in many ways, but it serves as a starting point for understanding the complex world of plugins within Gazebo.

You can use it in you desktop Gazebo or even inside the ROS Development Studio. It is also independent of the ROS version you are using (just install the proper packages).

 

Do you have any interesting modifications for this plugin? What about computing variance? Or what about computing illuminance by raytracing to lights? Please share your mods here!

You May Also Like…

41 Comments

  1. Álvaro Salcedo izqueirdo

    Hi users:
    First of all thanks for the tutorial.
    When I launch .launch file terminal says:

    gzserver: symbol lookup error: /home/alcor/catkin_ws/devel/lib/libgazebo_light_sensor_plugin.so: undefined symbol: _ZN6gazebo20GazeboRosCameraUtilsC2Ev
    [gazebo-2] process has died [pid 3163, exit code 255, cmd /home/alcor/catkin_ws/src/gazebo_light_sensor_plugin/scripts/run_gazebo /home/alcor/catkin_ws/src/gazebo_light_sensor_plugin/worlds/light.world __name:=gazebo __log:=/root/.ros/log/251944be-636a-11e5-af06-fcaa14787194/gazebo-2.log].
    log file: /root/.ros/log/251944be-636a-11e5-af06-fcaa14787194/gazebo-2*.log

    What am I doing wrong? or why does it happen?

    Thanks a lot

    Reply
    • Ricardo Téllez

      Hi Alvaro, sorry for answering you so late.
      Please follow the tutorial again with the updated code, since there were some errors in the code of the post that are now solved. Let me know if it works for you.

      Reply
  2. Steve Peters

    Does this package need to depend on `gazebo_plugins` also?

    Reply
    • Ricardo Téllez

      You are right Steve. Added to the post, thanks!

      Reply
  3. davidkim

    gzserver: symbol lookup error: /home/alcor/catkin_ws/devel/lib/libgazebo_light_sensor_plugin.so: undefined symbol: _ZN6gazebo20GazeboRosCameraUtilsC2Ev
    [gazebo-2] process has died [pid 3163, exit code 255, cmd /home/david/catkin_ws/src/gazebo_light_sensor_plugin/scripts/run_gazebo /home/david/catkin_ws/src/gazebo_light_sensor_plugin/worlds/light.world __name:=gazebo __log:=/root/.ros/log/251944be-636a-11e5-af06-fcaa14787194/gazebo-2.log].
    log file: /root/.ros/log/251944be-636a-11e5-af06-fcaa14787194/gazebo-2*.log

    i have same error…

    what should i do?

    Reply
    • Ricardo Téllez

      Hi Davidkim,
      I have detected some errors in the code posted here. I have just updated the code and included extra information. Please follow again the tutorial and let me know if the error is solved.

      Reply
      • davidkim

        i put light_sensor_plugin.h file in catkin_ws/src/gazebo_light_sensor_plugin/include/gazebo_light_sensor_plugin but when i catkin_make at catkin_ws. it said “cant find light_sensor_plugin.h”

        so i tried put it in catkin_ws/src/gazebo_light_sensor_plugin/include again it said same..

        i checked CMakelist.txt but it looks right because it wrote “include_directories(include)”

        i cant find what is wrong.

        Reply
        • Ricardo Téllez

          Hi David,
          the “light_sensor_plugin.h” file should be put inside “catkin_ws/src/gazebo_light_sensor_plugin/include/gazebo_light_sensor_plugin/” directory.

          Now, once you have it there, check that your CMakeLists.txt contains this:

          catkin_package(
          INCLUDE_DIRS include
          CATKIN_DEPENDS gazebo_plugins gazebo_ros roscpp
          )

          I think is that what you are missing.

          Reply
          • davidkim

            Yeah, it is working now and i saw some messages from “rostopic ehco /camera/rgb/image_raw”

            but 14.04 indigo version dont allow to use “rosrun image_view image_view image:=/camera/rgb/image_raw”

            so…

            i tried to see it on rviz but i cant see anything when i choose topic “camera/rgb/image_raw”

            i cant see it on rviz?

  4. Ricardo Téllez

    Davidkim,
    you should be able to use rosrun image_view image_view in Indigo without a problem, as well as rviz to display the camera.

    Once launched the simulation, type the following:
    > rostopic echo -n1 /camera/rgb/image_raw

    A list of data should appear on the screen if everything goes ok. If that is correct, then you can use the rosrun image_view or rviz to watch the image

    Reply
  5. Shikher Verma

    Thanks a lot for this tutorial. After three days for googling this is finally what worked.

    Reply
    • Ricardo Tellez

      Thank you Shikher for you support comments. Liked your page and projects about programming and robots. Would you mind to write a blog post for our blog sharing your experience with Gazebo, why you need it, how are you handling it, etc?

      Reply
  6. Adrien Thibault

    Hi, first thanks for this tutorial.

    I faced a problem when using the roslaunch command :
    “Invalid tag: gazebo_light_sensor_plugin
    ROS path [0]=/opt/ros/kinetic/share/ros
    ROS path [1]=/opt/ros/kinetic/share.

    Arg xml is
    The traceback for the exception was written to the log file”

    I am pretty sure I followed all the previous steps carefully !

    I then tried to launch gazebo manually using the command
    “gazebo –verbose src/gazebo_light_sensor_plugin/worlds/light.world”

    but received the error
    “[FATAL] [1488156372.811166473]: You must call ros::init() before creating the first NodeHandle
    Couldn’t find an AF_INET address for []
    Couldn’t find an AF_INET address for []
    [ERROR] [1488156372.834977386]: [registerPublisher] Failed to contact master at [:0]. Retrying…”

    after regular connection to the gazebo master

    I really do not know what to do now, thanks for your help.

    Reply
    • Adrien Thibault

      Nevermind, problem solved, just got confused and sourced the wrong file, now it works perfectly !

      Reply
  7. David Romanos

    Hi, first of all many thanks for the tutorial, it was really helpful for me.
    I am rather new in ROS, and I am trying to use a depth camera plugin. This plugin is defined in the same way you describe in the tutorial, meaning that it is defined as a ROS package and have the proper CMakeLists.txt and package.xml files. I am wondering now how to use this plugin with a robot I have defined in another package.
    I hope you can help me with this issue, thanks in advance.

    Reply
    • Shulace

      I’m not sure if you solved this already
      but, once you make your plugin, it becomes a binary file.
      If it’s a ros package, then that file should be under
      /{path to your catkin_ws}/devel/
      By this, you can use that plugin from where ever you want. Just declare the plugin just like how this tutorial has, under the element.
      Note: Make sure your plugin path is specified in the GAZEBO_PLUGIN_PATH variable.

      Reply
  8. shawn

    does it work with indigo+gazebo7? Does it raise compatible problems due to API change?

    Reply
  9. Larissa Nami

    Hi, first of all many thanks for the tutorial, it was really helpful for me.
    I’m trying to create a sonar sensor plugin, in the first steps I utilized the files that already exist in the gazebo on the sonar sensor. However I do not know how to create the file URCMakeList.txt, I tried to change a few things, but an error occurs, so I would like to know how you created this file.
    I hope you can help me, thanks in advance.

    Reply
    • Ricardo Tellez

      Hi Larissa,
      we just took the default CMakeLists.txt that is generated automatically when you create a package with catkin_create_pkg and then modified accordingly with what we needed.

      What I suggest you is that you have a look a the docs about CMakeLists.txt in the ROS wiki, here: http://wiki.ros.org/catkin/CMakeLists.txt
      Then, also have a look at the plugins repository of Gazebo (here https://github.com/ros-simulation/gazebo_ros_pkgs) and check what other CMakeLists.txt contain.

      Reply
  10. Steven

    Hello! Thanks a lot for the tutorial 🙂
    I have a couple of questions with your code.

    First, judging by the ‘for’ cycles, for me its no clear that you are adding all the pixels in the fov. I don’t understand what is happening with the StartingPix and the index variables. Can you please explain that to me? It would be very helpful.

    Second, I strongly believe that the final result of ‘illuminance’ is not in ‘lux’ unit. This because the average of the pixel intensity is very different to a luminous flux incident on a surface. So I think that the final result should be called ‘Average pixel intensity’. If I’m wrong, can you please explain to me how did you manage to convert RGB values to lux units?

    Much thanks for your work, it’s being very helpful, greetings!

    Reply
    • Steven

      Adding a little bit more:
      It’s important to distinguish between the ‘Illuminance’ and the ‘Average pixel intensity’ results. This because, with the current code, if you put two boxes – one box at a time- in the same exact spot, with the same simulated lights, but one box black and the other box white, the current ‘Illuminance’ result will change. This is wrong, since the light in the fov haven’t changed. But if you specify that the result of the code is ‘Average pixel intensity’ then it makes sense.

      Reply
  11. Halder A.

    Hi, thanks for this well-written tutorial.

    Secondly, is it possible to do this in python?

    Reply
  12. Mateo

    Hi, thanks for the tutorial. I am finding some problems when compiling the code with catkin_make. It says the following:

    /home/mateo/catkin_ws/src/gazebo_light_sensor_plugin/src/light_sensor_plugin.cpp:67:54: error: ‘class gazebo::sensors::Sensor’ has no member named ‘GetLastUpdateTime’
    this->sensor_update_time_ = this->parentSensor_->GetLastUpdateTime();
    ^
    /home/mateo/catkin_ws/src/gazebo_light_sensor_plugin/src/light_sensor_plugin.cpp:79:47: error: ‘class gazebo::physics::World’ has no member named ‘GetSimTime’
    common::Time cur_time = this->world_->GetSimTime();
    ^
    gazebo_light_sensor_plugin/CMakeFiles/gazebo_light_sensor_plugin.dir/build.make:62: recipe for target ‘gazebo_light_sensor_plugin/CMakeFiles/gazebo_light_sensor_plugin.dir/src/light_sensor_plugin.cpp.o’ failed

    Any idea on why is this happening?

    Reply
  13. Abdul Muhaimin Rahman

    Hello. Is there any way to write gazebo plugins in python?

    Reply
  14. Shulace

    Hi, thank you so much for the tutorial. It was a nice way to know more about the plugins.

    I just have a comment, nothing big.
    I was getting a warning about the GetLastUpdateTime function.
    I’m not sure since which version but this seems to have been replaced by LastUpdateTime function.
    Still works with the old one but I didn’t want the warning to come up each time I catkin_make it.

    Reply
    • Ricardo Tellez

      Yes you may be right. I did not check for the new version…
      Take into account that I wrote that post for Gazebo 4 and now we are already at Gazebo 10!

      The Gazebo guys usually change some things from version to version, and then the put as deprecated the old version. Deprecated means, the function is still there but it will be no longer available in future versions so you better migrate.

      You can try to change by the new function and see if it works

      Reply
  15. adam

    There’s a bunch of html code in here and i’m not 100% sure i can cleanly decode it, would it be possible to paste the code in without the > etc?

    Reply
  16. Stephen DeRosa

    Hi,

    I am able to get the plugin to work, however it only actively publishes the illuminance while “rosrun image_view image_view image:=…” is running. Is it supposed to be like this?

    Thanks.

    Reply
    • Ricardo Tellez

      Yes that is correct. This means that it will only publish when there is another program listening to the topic. That saves CPU

      Reply
  17. silahkan cek untuk info lebih lengkap

    An outstanding share! I have just forwarded this onto a coworker who has been conducting a little homework on this.
    And he actually bought me dinner because I found it for him…

    lol. So allow me to reword this…. Thank YOU for the meal!!

    But yeah, thanx for spending the time to discuss
    this topic here on your internet site.

    Reply
  18. unsplash.com

    Thanks , I’ve recently been looking for info about this topic for
    a while and yours is the best I’ve discovered so far.

    However, what about the conclusion? Are you positive concerning the source?

    Reply
  19. sustainable travel

    you’re actually a excellent webmaster. The site loading pace is incredible.
    It sort of feels that you’re doing any distinctive
    trick. Also, The contents are masterwork. you’ve done a excellent activity in this matter!

    Reply
  20. Taj

    When discussing responsive website design composition, most originators and
    developers consider websites on cell phones, tablets,
    and cell PCs. On a typical cell phone with a 3-by-four numeric
    keypad, as an example, where no less than three letters are assigned to a number, you usually should punch via every key a
    number of occasions to get one phrase on the display screen. For passable end result, feel free
    to intake no less than one teaspoon of bee pollen each day.
    Nowadays, individuals carry a digital camera, or a minimum of a
    VGA phone, to take footage. Make your computer faster with
    more reminiscence and take management of your
    computer. They take up much less room on the countertop or
    in the cabinet once they do get put away. Women get easily attracted in the direction of men who’re protecting and highly effective.

    Who doesn’t like LED lights on their drum pads of their darkish studio at night?

    How much space do you may have in your studio?

    Reply
  21. baca selanjutnya

    This design is incredible! You certainly know how to keep a reader entertained.

    Between your wit and your videos, I was almost moved to
    start my own blog (well, almost…HaHa!) Great job. I really loved
    what you had to say, and more than that, how you presented it.
    Too cool!

    Reply
  22. Jesus Yepez

    Hello, is there a chance that you could make an update for the tutorial, I’m triying to execute with Ubuntu 18.04, ROS melodic and gazebo9. It give me erros related to the code itself so my guess is that the syntaxis have changed.

    Reply

Submit a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

FOR CAMPUS

ROS & Robotics Curriculum Designed for Remote Teaching

Ready for your Robotics career?

Create an account, and start learning and developing robots

Share This