Custom messages - modulabs/gazebo-tutorial GitHub Wiki
Gazebo ํญ๋ชฉ์ Google ํ๋กํ ์ฝ ๋ฉ์์ง๋ฅผ ํตํด ํต์ ํฉ๋๋ค. ๊ฐ์ ๋ณด๊ฐ ์ ๊ณตํ๋ ๊ด๋ฒ์ํ ๋ฉ์์ง ์ ํ ๋ชฉ๋ก์ด ๊ฐ์ ๋ณด ์ฃผ์ ๊ตฌ๋ ๋ฐ ๊ฒ์์ ์ฌ์ฉ๋ฉ๋๋ค. ๊ทธ๋ฌ๋, ๋น์ ์ด ๋น์ ์์ ์ ๊ฒ์ ๋ง๋ค๊ณ ์ถ์ดํ๋ ๋ง์ ์ํฉ๋ค์ด์๋ค.
์ด ์์ต์๋ ์ฌ์ฉ์ ์ ์ ๋ฉ์์ง๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ๊ณผ Gazebo ํ๋ฌ๊ทธ์ธ์์ ๊ตฌ๋ ํ๊ณ ๊ฒ์ํ๋ ๋ฐฉ๋ฒ์ ์์ ๋๋ค.
์ด ํ๋ก์ ํธ๊ฐ ๋๋๋ฉด Gazebo World์ 2D ์ถฉ๋ ๋งต์ ๋ง๋ค ์์๋ ํ๋ฌ๊ทธ์ธ์ด ์์ด์ผํฉ๋๋ค. ํ๋ฌ๊ทธ ์ธ์ Gazebo์์ ์ธ๊ณ๋ฅผ ๋์คํฐ ํํฉ๋๋ค. RayShape๋ฅผ ์ฌ์ฉํ์ฌ์ด ๊ฒฉ์ ์๋์์ ๊ด์ ๊ต์ฐจ๋ฅผํฉ๋๋ค. ์์ฑ ๋ ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉ์ ์ ์ ํญ๋ชฉ์ ๊ฒ์ํ๊ณ ํ์ผ๋ก ์ถ๋ ฅํฉ๋๋ค. ์ด ํ๋ฌ๊ทธ์ธ์ ์์ค ์ฝ๋๋ BitBucket์ ์์ต๋๋ค.
๊ฐ์ ์ ์ํด ๋ฌธ์ ๋ฅผ ์ ๊ธฐํ๊ฑฐ๋ ์์ฒญ์ ์ ์ถํ์ญ์์ค. ์ฝ๋์ ์๋ ์์ฑ์๋ Stephen Brawner์ ๋๋ค.
mercurial์ ์ฌ์ฉํ์ฌ ์์ค ์ฝ๋๋ฅผ ๋ค์ด๋ก๋ ํ ์ ์์ต๋๋ค.
hg clone https://bitbucket.org/osrf/collision_map_creator_plugin
๋๋ ์ฝ๋๋ฅผ ๋ณต์ฌํ์ฌ ์๋ ์ค๋ช ๋๋๋ก ํ์ผ์ ๋ถ์ฌ ๋ฃ์ผ์ญ์์ค.
Gazebo์ ์ด๋ฏธ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฉ์์ง ์ ํ ์ค ํ๋์ ๊ตฌ๋ ์ / ๊ฒ์์ ๋ฉ์์ง๋ฅผ ๋ฐ์ด ๋ฃ๊ธฐ๊ฐ ๋๋ฌด ์ด๋ ต๋ค๋ฉด ์์ ์ ๋ฉ์์ง๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ๋ํ ๋ณต์กํ ๋ฉ์์ง๋ฅผ ์์ฑํ๋ ค๋ ๊ฒฝ์ฐ ๋ค์ํ ๋ฉ์์ง ์ ํ์ ๊ฒฐํฉ ํ ์ ์์ต๋๋ค. Gazebo์๋ ์ด๋ฏธ ๋ฉ์์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. ์ค์น๋ ๋ฉ์์ง๋ ๋ฐ๋น์ ์ค์น๋ฅผ ์ํด / usr / include / gazebo- / gazebo / msgs / proto์์ ์ฐพ์ ์ ์์ต๋๋ค. ์ด ์์ต์์์๋ vector2d.proto ๋ฉ์์ง๋ฅผ ์ฌ์ฉํฉ๋๋ค.
~ / collision_map_creator_plugin / msgs / collision_map_request.proto๋ฅผ๋ณด๊ณ ๋ง์ถค ๋ฉ์์ง๊ฐ ์ด๋ป๊ฒ ์ ์ธ๋๋์ง ํ์ธํ ์ ์์ต๋๋ค.
package collision_map_creator_msgs.msgs;
import "vector2d.proto";
message CollisionMapRequest
{
required gazebo.msgs.Vector2d upperLeft = 1;
required gazebo.msgs.Vector2d upperRight = 2;
required gazebo.msgs.Vector2d lowerRight = 3;
required gazebo.msgs.Vector2d lowerLeft = 4;
required double height = 5;
required double resolution = 6;
optional string filename = 7 [default = ""];
optional int32 threshold = 8 [default = 255];
}
๋จผ์ package ์ ์ธ๊ณผ ํจ๊ป์ด ๋ฉ์์ง๊ฐ ์ด์์๋ ๋ค์ ์คํ์ด์ค๋ฅผ ์ ์ธํฉ๋๋ค.
package collision_map_creator_msgs.msgs;
Gazebo์ vector2d๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ์ฐ๋ฆฌ๋ ๊ทธ๊ฒ์ ํฌํจํด์ผํฉ๋๋ค (๋ฉ์์ง์ ์์น๋ CMakeLists.txt์์ ์ฒ๋ฆฌ๋ฉ๋๋ค).
import "vector2d.proto";
์ด๊ฒ์ด ์ค์ ๋ฉ์์ง ๊ตฌ์กฐ์ ๋๋ค.
message CollisionMapRequest{}
๊ฐ ๋ฉ์ธ์ง ํ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ ์ธํ์ญ์์ค.
["optional"/"required"] [field type] [field name] = [enum];
๊ฐ ์ด๊ฑฐ ํ์ ๊ณ ์ ํ ์ซ์ ์ฌ์ผํฉ๋๋ค. ์ ์ฒด ํจํค์ง ์ด๋ฆ์ ์ธ๋ถ ๋ฉ์์ง (์ด ๊ฒฝ์ฐ gazebo.msgs)๋ฅผ ์ง์ ํ๋ ๋ฐ ์ฌ์ฉํด์ผํฉ๋๋ค.
required gazebo.msgs.Vector2d upperLeft = 1; required gazebo.msgs.Vector2d upperRight = 2; required gazebo.msgs.Vector2d lowerRight = 3; required gazebo.msgs.Vector2d lowerLeft = 4; required double height = 5; required double resolution = 6;
๋ฉ์์ง์๋ ์ ํ์ ํ๋๊ฐ ํฌํจ๋ ์ ์์ต๋๋ค. ์ ํ์ ํ๋๊ฐ ์ง์ ๋์ง ์์ ๊ฒฝ์ฐ ์ ํ์ ํ๋์ ๊ธฐ๋ณธ๊ฐ์ด ํฌํจ๋ ์ ์์ต๋๋ค. ์ด๊ฒ์ ์ด๋ป๊ฒ ๊ทธ ๋ฐฉ๋ฒ์ ์์ ๋๋ค.
optional string filename = 7 [default = ""]; optional int32 threshold = 8 [default = 255];
์ด ํํ ๋ฆฌ์ผ์์๋ protobuf ๋ฉ์์ง ์ ์์์ C ++ ์ฝ๋๋ฅผ ์์ฑ ํ ~/collision_map_creator/msgs/CMakeLists.txt ๋ ์ ๊ณตํฉ๋๋ค.
CMake์๊ฒ Protobuf ํจํค์ง๋ฅผ ํฌํจํ๋๋ก ์๋ ค์ค๋๋ค.
find_package(Protobuf REQUIRED)
์ด ์น์ ์์๋ ๋ฉ์์ง๊ฐ ์ค์น๋ ์์น๋ฅผ ์ฐพ์ต๋๋ค. ๋ฉ์์ง ํ์ผ์ ๋ฐ๋น์ ์ค์น๋ฅผ ์ํด /usr/include/gazebo-<YOUR_GAZEBO_VERSION>/gazebo/msgs/proto ์ ์ ์ฅ๋์ง๋ง ์ฌ์ฉ์๊ฐ ๋ค๋ฅธ ๊ณณ์ ์ค์นํ์ ์๋ ์์ต๋๋ค. ์ด ์ฝ๋๋ gazebo-XX ํด๋๋ฅผ ๊ฒ์ํ๊ณ ์ด์ ๋ฐ๋ผ PROTOBUF_IMPORT_DIRS ๋ณ์๋ฅผ ์ค์ ํฉ๋๋ค. PROTOBUF_IMPORT_DIRS ๋ ์ถ๊ฐ ๋ฉ์์ง ํ์ผ์ ์์น๋ฅผ โโ์ง์ ํฉ๋๋ค.
set(PROTOBUF_IMPORT_DIRS)
foreach(ITR ${GAZEBO_INCLUDE_DIRS})
if(ITR MATCHES ".*gazebo-[0-9.]+$")
set(PROTOBUF_IMPORT_DIRS "${ITR}/gazebo/msgs/proto")
endif()
endforeach()
vector2d.proto ๋ฉ์์ง, dependencies time.proto ๋ฐ header.proto ๋ฐ collision_map_request.proto ๋ฉ์์ง์ ํจ๊ป msgs ๋ผ๋ ๋ชฉ๋ก์ ์์ฑํฉ๋๋ค.
set (msgs
collision_map_request.proto
${PROTOBUF_IMPORT_DIRS}/vector2d.proto
${PROTOBUF_IMPORT_DIRS}/header.proto
${PROTOBUF_IMPORT_DIRS}/time.proto
)
๋ฉ์์ง์ ํ์ํ C ++ ํค๋์ ์์ค ํ์ผ์ ๋น๋ํ์ญ์์ค.
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${msgs})
์ด ๋ฉ์์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ์ฌ ๋ค๋ฅธ ํ๋ก์ ํธ์ ๋งํฌ์ ์ฐ๊ฒฐํ ์ ์์ต๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๋ฆ์ ์๋์ ๋ค๋ฅธ CMakeLists.txt ์ง์ ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๋ฆ๊ณผ ๋์ผํด์ผํฉ๋๋ค.
add_library(collision_map_creator_msgs SHARED ${PROTO_SRCS})
target_link_libraries(collision_map_creator_msgs ${PROTOBUF_LIBRARY})
์ด๊ฒ์ ์ฌ์ฉ์ ์ ์ Gazebo ์๋ ํ๋ฌ๊ทธ์ธ ( ~/collision_map_creator_plugin/collision_map_creator.cc )์ ์ฝ๋์ ๋๋ค.
#include <iostream>
#include <math.h>
#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io/png_dynamic_io.hpp>
#include <boost/shared_ptr.hpp>
#include <sdf/sdf.hh>
#include <ignition/math/Vector3.hh>
#include "gazebo/gazebo.hh"
#include "gazebo/common/common.hh"
#include "gazebo/msgs/msgs.hh"
#include "gazebo/physics/physics.hh"
#include "gazebo/transport/transport.hh"
#include "collision_map_request.pb.h"
namespace gazebo
{
typedef const boost::shared_ptr<
const collision_map_creator_msgs::msgs::CollisionMapRequest>
CollisionMapRequestPtr;
class CollisionMapCreator : public WorldPlugin
{
transport::NodePtr node;
transport::PublisherPtr imagePub;
transport::SubscriberPtr commandSubscriber;
physics::WorldPtr world;
public: void Load(physics::WorldPtr _parent, sdf::ElementPtr _sdf)
{
node = transport::NodePtr(new transport::Node());
world = _parent;
// Initialize the node with the world name
node->Init(world->Name());
std::cout << "Subscribing to: " << "~/collision_map/command" << std::endl;
commandSubscriber = node->Subscribe("~/collision_map/command",
&CollisionMapCreator::create, this);
imagePub = node->Advertise<msgs::Image>("~/collision_map/image");
}
public: void create(CollisionMapRequestPtr &msg)
{
std::cout << "Received message" << std::endl;
std::cout << "Creating collision map with corners at (" <<
msg->upperleft().x() << ", " << msg->upperleft().y() << "), (" <<
msg->upperright().x() << ", " << msg->upperright().y() << "), (" <<
msg->lowerright().x() << ", " << msg->lowerright().y() << "), (" <<
msg->lowerleft().x() << ", " << msg->lowerleft().y() <<
") with collision projected from z = " <<
msg->height() << "\nResolution = " << msg->resolution() << " m\n" <<
"Occupied spaces will be filled with: " << msg->threshold() <<
std::endl;
double dX_vertical = msg->upperleft().x() - msg->lowerleft().x();
double dY_vertical = msg->upperleft().y() - msg->lowerleft().y();
double mag_vertical =
sqrt(dX_vertical * dX_vertical + dY_vertical * dY_vertical);
dX_vertical = msg->resolution() * dX_vertical / mag_vertical;
dY_vertical = msg->resolution() * dY_vertical / mag_vertical;
double dX_horizontal = msg->upperright().x() - msg->upperleft().x();
double dY_horizontal = msg->upperright().y() - msg->upperleft().y();
double mag_horizontal =
sqrt(dX_horizontal * dX_horizontal + dY_horizontal * dY_horizontal);
dX_horizontal = msg->resolution() * dX_horizontal / mag_horizontal;
dY_horizontal = msg->resolution() * dY_horizontal / mag_horizontal;
int count_vertical = mag_vertical / msg->resolution();
int count_horizontal = mag_horizontal / msg->resolution();
if (count_vertical == 0 || count_horizontal == 0)
{
std::cout << "Image has a zero dimensions, check coordinates"
<< std::endl;
return;
}
double x,y;
boost::gil::gray8_pixel_t fill(255-msg->threshold());
boost::gil::gray8_pixel_t blank(255);
boost::gil::gray8_image_t image(count_horizontal, count_vertical);
double dist;
std::string entityName;
ignition::math::Vector3d start, end;
start.Z(msg->height());
end.Z(0.001);
gazebo::physics::PhysicsEnginePtr engine = world->Physics();
engine->InitForThread();
gazebo::physics::RayShapePtr ray =
boost::dynamic_pointer_cast<gazebo::physics::RayShape>(
engine->CreateShape("ray", gazebo::physics::CollisionPtr()));
std::cout << "Rasterizing model and checking collisions" << std::endl;
boost::gil::fill_pixels(image._view, blank);
for (int i = 0; i < count_vertical; ++i)
{
std::cout << "Percent complete: " << i * 100.0 / count_vertical
<< std::endl;
x = i * dX_vertical + msg->lowerleft().x();
y = i * dY_vertical + msg->lowerleft().y();
for (int j = 0; j < count_horizontal; ++j)
{
x += dX_horizontal;
y += dY_horizontal;
start.X(x);
end.X(x);
start.Y(y);
end.Y(y);
ray->SetPoints(start, end);
ray->GetIntersection(dist, entityName);
if (!entityName.empty())
{
image._view(i,j) = fill;
}
}
}
std::cout << "Completed calculations, writing to image" << std::endl;
if (!msg->filename().empty())
{
boost::gil::gray8_view_t view = image._view;
boost::gil::png_write_view(msg->filename(), view);
}
}
};
// Register this plugin with the simulator
GZ_REGISTER_WORLD_PLUGIN(CollisionMapCreator)
}
ํฌํจ ํ ํ์ ์์คํ ํค๋
#include <iostream>
#include <math.h>
#include <boost/shared_ptr.hpp>
์ด๊ฒ๋ค์ ์ฐ๋ฆฌ๊ฐ ํ์๋กํ๋ ํ์ํ ๊ฐ์ ๋ณด (Gazebo) ํค๋์ ๋๋ค.
#include "gazebo/gazebo.hh"
#include "gazebo/common/common.hh"
#include "gazebo/math/Vector3.hh"
#include "gazebo/msgs/msgs.hh"
#include "gazebo/physics/physics.hh"
#include "gazebo/transport/transport.hh"
#include "collision_map_request.pb.h"
์ฐ๋ฆฌ์ collision_map_request ๋ฉ์์ง๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์์ฑ ๋ ํค๋ ํ์ผ์ ํฌํจ์์ผ์ผํฉ๋๋ค.
#include "collision_map_request.pb.h"
์ด๋ค์ .png ํ์ผ์ ์ฐ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io/png_dynamic_io.hpp>
ํ๋ฌ๊ทธ์ธ์ Gazebo ๋ค์ ์คํ์ด์ค์ ์์ต๋๋ค.
namespace gazebo
{
์ด๊ฒ์ ์ฝ๋ฐฑ ๋ฉ์๋์ ์ ์ก ๋ ๊ฐ์ฒด์ ๋๋ค. ์ด๋ฅผ์ํ typedef๋ฅผ ๋ง๋๋ ๊ฒ์ด ํธ๋ฆฌํฉ๋๋ค.
typedef const boost::shared_ptr<const collision_map_creator_msgs::msgs::CollisionMapRequest> CollisionMapRequestPtr;
Gazebo WorldPlugin์ ํ์ ํด๋์ค ๋ง๋ค๊ธฐ. NodePtr , PublisherPtr , SubcriberPtr ๋ฐ WorldPtr ์ ์ ์งํด์ผํ๋ฏ๋ก ํด๋์ค ๋ณ์๋ก ์ถ๊ฐํด์ผํฉ๋๋ค.
class CollisionMapCreator : public WorldPlugin { transport::NodePtr node; transport::PublisherPtr imagePub; transport::SubscriberPtr commandSubscriber; physics::WorldPtr world;
World Plugin์ Load ๋ฉ์๋. ๋ ธ๋, ์ธ๊ณ, ๊ตฌ๋ ์ ๋ฐ ๊ฒ์์๋ฅผ ์ค์ ํฉ๋๋ค.
public: void Load(physics::WorldPtr _parent, sdf::ElementPtr _sdf)
{
node = transport::NodePtr(new transport::Node());
world = _parent;
// Initialize the node with the world name
node->Init(world->GetName());
std::cout << "Subscribing to: " << "~/collision_map/command" << std::endl;
node->Subscribe ์ ์ฃผ์ , ์ฝ๋ฐฑ ํจ์ ๋ฐ์ด ํ๋ฌ๊ทธ์ธ ๊ฐ์ฒด์ ๋ํ ํฌ์ธํฐ๋ก ํธ์ถ๋ฉ๋๋ค. node->Advertise , ๋ฉ์์ง ์ ํ์ ํ ํ๋ฆฌํธ ์ธ์ ๋ฐ ๊ด๊ณ ํ ์ฃผ์ ๋งค๊ฐ ๋ณ์๊ฐ ํ์ํฉ๋๋ค.
commandSubscriber = node->Subscribe("~/collision_map/command", &CollisionMapCreator::create, this);
imagePub = node->Advertise<msgs::Image>("~/collision_map/image");
์ด๊ฒ์ ์ฝ๋ฐฑ ๋ฉ์๋์ ๋๋ค. ์ด์ ์ ์ฌ์ฉํ typedef๋ฅผ ํฌํจํฉ๋๋ค. ์ฝ๋ฐฑ์๋ ์ฐธ์กฐ๊ฐ ์ ๋ฌ๋๋ฏ๋ก &msg ๋ฅผ ์ฌ์ฉํด์ผํฉ๋๋ค.
public: void create(CollisionMapRequestPtr &msg)
CollisionMapRequestPtr ์ shared_ptr์ ๋์ด๊ธฐ ๋๋ฌธ์ ์ค์ ํ๋๋ฅผ ์ป์ผ๋ ค๋ฉด ์๋์์์ ๊ฐ์ด ๊ตฌ์กฐ ์ฐธ์กฐ ( '.') ๋์ ๊ตฌ์กฐ์ฒด ์ฐธ์กฐ ํด์ ( '->')๋ฅผ ์ฌ์ฉํด์ผํฉ๋๋ค. ๊ฐ์ฒด์ ํ๋๋ฅผ ์ป์ผ๋ ค๋ฉด get ํจ์๋ฅผ ํธ์ถํด์ผํฉ๋๋ค (์ : field() ).
double z = msg->height();
๊ทธ๋ฌ๋ ์ ๊น ๋ฌผ์ด๋ณด์ญ์์ค. ์ ์ง์ ํ์ ํ๋๋ฅผ ์ฐธ์กฐ ํ ์ ์์ต๋๊น? ๊ทธ๊ฒ์ shared_ptrs๊ฐ ๋ด๋ถ์ ์๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ๋ณด๋ธ ๋ฉ์์ง์ ๋๋ค.
double dX_vertical = msg->upperleft().x() - msg->lowerleft().x();
double dY_vertical = msg->upperleft().y() - msg->lowerleft().y();
double mag_vertical = sqrt(dX_vertical * dX_vertical + dY_vertical * dY_vertical);
dX_vertical = msg->resolution() * dX_vertical / mag_vertical;
dY_vertical = msg->resolution() * dY_vertical / mag_vertical;
์ด๊ฒ์ ์ฌ์ฉ ๋ ๋ฌผ๋ฆฌ ์์ง์ ํธ์ถํ์ฌ ๋จ์ผ ๊ด์ ์ถ์ ์ ์ํํ๋ ๋ฐฉ๋ฒ์ ์์ ๋๋ค. ์ด ์ฝ๋๋ gazebo::physics::World::GetEntityBelowPoint(Vector3)
gazebo::physics::PhysicsEnginePtr engine = world->GetPhysicsEngine();
engine->InitForThread();
gazebo::physics::RayShapePtr ray = boost::dynamic_pointer_cast<gazebo::physics::RayShape>(
engine->CreateShape("ray", gazebo::physics::CollisionPtr()));
๊ฐ์ฒด๊ฐ์๋ ๊ณณ๊ณผ์๋ ๊ณณ์ ๊ฒฐ์ ํ๊ธฐ ์ํด ๊ฒฉ์๋ฅผ ๋์คํฐ ํํฉ๋๋ค.
for (int i = 0; i < count_vertical; ++i)
{
... for (int j = 0; j < count_horizontal; ++j) { ... } ... }
์ด๋ ๊ฒํ๋ฉด ์ด๋ฏธ์ง ๋ฉ์์ง๊ฐ ๋ง๋ค์ด์ง๊ณ ํ์ํ ํ๋๊ฐ ์ฑ์์ง๋๋ค. ๊ฐ ํ๋์ ๋ํ setter ๋ฉ์๋๋ set_field(something) ์ ๊ฐ์ ํ์์ผ๋ก๋์ด ์์ต๋๋ค.
msgs::Image image;
image.set_width(count_horizontal);
image.set_height(count_vertical);
image.set_pixel_format(0);
image.set_step(count_horizontal);
image.set_data(data);
imagePub->Publish(image);
ํ๋ฌ๊ทธ์ธ์ ์๋ฎฌ๋ ์ดํฐ์ ๋ฑ๋กํ์ญ์์ค.
// Register this plugin with the simulator GZ_REGISTER_WORLD_PLUGIN(CollisionMapCreator) }
์ด ์คํ ํ์ผ์ ์ ๋ง๋ ์ธ๋ถ์์ ์คํ๋์ง๋ง Gazebo์์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ ธ ์์ ์ฌ์ฉ์ ์ ์ ๋น๋ ๋ ๋ฉ์์ง ์ ํ์ผ๋ก ์ ๋ง๋ ํญ๋ชฉ์ ๊ฒ์ํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค. ํ๋ฌ๊ทธ์ธ ์์ต์์์ ์ค๋ช ํ์ง ์์ ๋ช ๊ฐ์ง ์ถ๊ฐ ๋จ๊ณ๊ฐ ํ์ํฉ๋๋ค. ~/collision_map_creator_plugin/request_publisher.cc ํ์ผ์ ์ด์ด ์์ค ์ฝ๋๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
#include #include <math.h> #include #include <sdf/sdf.hh>
#include "gazebo/gazebo.hh" #include "gazebo/common/common.hh" #include "gazebo/transport/transport.hh" #include "gazebo/physics/physics.hh" #include "gazebo/msgs/msgs.hh"
#include "collision_map_request.pb.h" #include "vector2d.pb.h"
using namespace std;
bool createVectorArray(const char * vectorString, dequegazebo::msgs::Vector2d* corners) { dequegazebo::msgs::Vector2d*::iterator it;
string cornersStr = vectorString;
size_t opening=0;
size_t closing=0;
for (it = corners.begin(); it != corners.end(); ++it)
{
opening = cornersStr.find('(', closing);
closing = cornersStr.find(')', opening);
if (opening == string::npos || closing == string::npos)
{
std::cout << "Poorly formed string: " << cornersStr << std::endl;
std::cout << "( found at: " << opening << " ) found at: " << closing << std::endl;
return false;
}
string oneCornerStr = cornersStr.substr(opening + 1, closing - opening - 1);
size_t commaLoc = oneCornerStr.find(",");
string x = oneCornerStr.substr(0,commaLoc);
string y = oneCornerStr.substr(commaLoc + 1, oneCornerStr.length() - commaLoc);
(*it)->set_x(atof(x.c_str()));
(*it)->set_y(atof(y.c_str()));
}
return true;
}
int main(int argc, char * argv[]) { if (argc > 4) { collision_map_creator_msgs::msgs::CollisionMapRequest request; dequegazebo::msgs::Vector2d* corners;
corners.push_back(request.mutable_upperleft());
corners.push_back(request.mutable_upperright());
corners.push_back(request.mutable_lowerright());
corners.push_back(request.mutable_lowerleft());
if (!createVectorArray(argv[1],corners))
{
return -1;
}
request.set_height(atof(argv[2]));
request.set_resolution(atof(argv[3]));
request.set_filename(argv[4]);
if (argc == 6)
{
request.set_threshold(atoi(argv[5]));
}
gazebo::transport::init();
gazebo::transport::run();
gazebo::transport::NodePtr node(new gazebo::transport::Node());
node->Init("default");
std::cout << "Request: " <<
" UL.x: " << request.upperleft().x() <<
" UL.y: " << request.upperleft().y() <<
" UR.x: " << request.upperright().x() <<
" UR.y: " << request.upperright().y() <<
" LR.x: " << request.lowerright().x() <<
" LR.y: " << request.lowerright().y() <<
" LL.x: " << request.lowerleft().x() <<
" LL.y: " << request.lowerleft().y() <<
" Height: " << request.height() <<
" Resolution: " << request.resolution() <<
" Filename: " << request.filename() <<
" Threshold: " << request.threshold() << std::endl;
gazebo::transport::PublisherPtr imagePub =
node->Advertise<collision_map_creator_msgs::msgs::CollisionMapRequest>(
"~/collision_map/command");
imagePub->WaitForConnection();
imagePub->Publish(request);
gazebo::transport::fini();
return 0;
}
return -1;
}
ํ์ํ ํ์ค c ++ ํค๋ ํฌํจ
#include #include <math.h> #include <boost/shared_ptr.hpp> #include <sdf/sdf.hh> #include <boost/gil/gil_all.hpp> #include <boost/gil/extension/io/png_dynamic_io.hpp> ์ด๊ฒ๋ค์ ์ฐ๋ฆฌ ์คํ ํ์ผ์ ํ์ํ ์ ๋ง๋ ํค๋์ ๋๋ค. #include "gazebo/gazebo.hh" #include "gazebo/common/common.hh" #include "gazebo/math/Vector3.hh" #include "gazebo/transport/transport.hh" #include "gazebo/physics/physics.hh" #include "gazebo/msgs/msgs.hh" #include "collision_map_request.pb.h" ์ด๊ฒ์ ์ฌ์ฉ์ ์ ์ ๋ฉ์์ง ์ ํ๊ณผ ์ฐ๊ด๋ ํค๋์ ๋๋ค. #include "collision_map_request.pb.h"
์ด ํจ์๋ "(x1, y1) (x2, y2) (x3, y3) (x4, y4)"์ ์ํด ์ ์ ๋ ์ขํ ๋ฌธ์์ด์ Vector2d ๋ฉ์์ง์ ์ํค๋ก ํ์ฑํฉ๋๋ค.
bool createVectorArray(const char * vectorString, dequegazebo::msgs::Vector2d* corners) deque ๋ฐ๋ณต์๋ฅผ ์ด๊ธฐํํฉ๋๋ค. dequegazebo::msgs::Vector2d*::iterator it;
Iterate through the corners deque
... for (it = corners.begin(); it != corners.end(); ++it) { ... } ์ฝ๋ ์ธ Vector2d์ ๋ํด ์ฐ๋ฆฌ๋ x์ y์ ๋ํด ๋ฐ๊ฒฌ ๋ ๋ฌธ์์ด์ float๋ก ๋ณํํ์ฌ X์ Y๋ฅผ ์ค์ ํฉ๋๋ค (*it)->set_x(atof(x.c_str())); (*it)->set_y(atof(y.c_str()));
The main function, which is called when the executable is run.
int main(int argc, char * argv[]) { ... } ์ด ์คํ ํ์ผ์๋ ์ฌ๋ฌ ๊ฐ์ง ์ธ์๊ฐ ํ์ํ๋ฉฐ ์ ์ด๋ 4 ๊ฐ ์ด์์ธ ๊ฒฝ์ฐ์๋ง ์งํํ์ญ์์ค. if (argc > 4) { ... } CollisionMapRequest ๋ฉ์์ง๋ฅผ ์์ฑํฉ๋๋ค. ์ด ๋ฉ์์ง๋ฅผ ๋ณด๋ด๊ธฐ ์ ์ ์์ฑํด์ผํฉ๋๋ค. ๋ํ Vector2d ๋ฉ์์ง์ deque๋ฅผ ์ด๊ธฐํํ์ฌ createVectorArray ํจ์์ ์ ๋ฌํ ์ ์์ต๋๋ค. collision_map_creator_msgs::msgs::CollisionMapRequest request; dequegazebo::msgs::Vector2d* corners; ๋ฉ์์ง ํ๋๊ฐ ๋จ์ํ ๊ฒฝ์ฐ (double, int32, string) ์ฐ๊ด๋ set_field() ๋ฉ์๋๋ก ์ง์ ์ค์ ํ ์ ์์ต๋๋ค. ํ๋๊ฐ ๋ฉ์์ง ์ ํ ์์ฒด ์ธ ๊ฒฝ์ฐ, ํด๋น mutable_field() ๋ฉ์๋๋ฅผ ํตํด ์ก์ธ์คํด์ผํฉ๋๋ค. Vector2d๋ ๊ทธ ์์ฒด๋ก ๋ณ๋์ ๋ฉ์์ง์ด๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๊ฐ ์ฌ๊ธฐ์์๋ ๊ฒ์ฒ๋ผ ๊ฐ๋ณ ํจ์ ํธ์ถ๋ก๋ถํฐ ๋จผ์ ํฌ์ธํฐ๋ฅผ ๊ฐ์ ธ์์ผํฉ๋๋ค.
corners.push_back(request.mutable_upperleft());
corners.push_back(request.mutable_upperright());
corners.push_back(request.mutable_lowerright());
corners.push_back(request.mutable_lowerleft());
Vector2d ํฌ์ธํฐ์ deque์ ์ฒซ ๋ฒ์งธ argv ๋ฌธ์์ด์ createVectorArray ํฉ๋๋ค. ์คํจ ํ ๊ฒฝ์ฐ ์ ๋ ฅ ํ์์ด ์๋ชป ์ง์ ๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ด ๊ฒฝ์ฐ ํ๋ก๊ทธ๋จ์ ์ข ๋ฃํ์ญ์์ค. if (!createVectorArray(argv[1],corners)) { return -1; } ๊ฐ๋จํ ํ๋์ ๊ฒฝ์ฐ set_field ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๊ฐ์ ์ง์ ์ค์ ํ ์ ์์ต๋๋ค.
request.set_height(atof(argv[2]));
request.set_resolution(atof(argv[3]));
request.set_filename(argv[4]);
์ ํ์ ์๊ณ ๊ฐ ํ๋์ ๊ฒฝ์ฐ ๋ช ๋ น ์ธ์์ ์ง์ ๋ ๊ฒฝ์ฐ์ด๋ฅผ ์ค์ ํฉ๋๋ค. if (argc == 6) { request.set_threshold(atoi(argv[6])); }
์ด๊ฒ์ ์ฃผ์ ๋ฐฉ๋ฒ์ ๋งค์ฐ ์ค์ํ ๋ถ๋ถ์ ๋๋ค. ์ด๊ฒ์ gazebo ์ ์ก ์์คํ ์ ์ด๊ธฐํํฉ๋๋ค. ํ๋ฌ๊ทธ์ธ์ ์ํด, Gazebo๋ ์ด๋ฏธ ์ด๊ฒ์ ์ฒ๋ฆฌํ์ง๋ง, ์ฐ๋ฆฌ ์์ ์ ์คํ ํ์ผ์ ์ํด ์ฐ๋ฆฌ๋ ์ค์ค๋ก ํด๊ฒฐํด์ผํฉ๋๋ค. ๋ํ ์ฐ๋ฆฌ๊ฐ ์ ์ ์ฌ์ผ ๋ค์ ์คํ์ด์ค๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ช ์ํด์ผํฉ๋๋ค.
gazebo::transport::init();
gazebo::transport::run();
gazebo::transport::NodePtr node(new gazebo::transport::Node());
node->Init("default");
~/collision_map/command ํญ๋ชฉ์์ ์์ฒญ ๊ฒ์์๋ฅผ ์ด๊ธฐํํฉ๋๋ค.
gazebo::transport::PublisherPtr imagePub =
node->Advertise<collision_map_creator_msgs::msgs::CollisionMapRequest>(
"~/collision_map/command");
๋ฉ์์ง๋ฅผ ๊ฒ์ํ๋ ค๋ฉด ๋จผ์ ๊ฒ์์๊ฐ ๋ง์คํฐ์ ์ฐ๊ฒฐ๋ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ์ฐ๋ฆฌ๋ ์ง์ ๋ฉ์์ง๋ฅผ ๊ฒ์ ํ ์ ์์ต๋๋ค.
imagePub->WaitForConnection();
imagePub->Publish(request);
์ข ๋ฃํ๊ธฐ ์ ์, ํ๋ก๊ทธ๋จ์ transport::fini() ๋ฅผ ํธ์ถํ์ฌ ๋ชจ๋ ๊ฒ์ ๋ผ์ด ๋ด์ผํ๋ค. gazebo::transport::fini();
์๋์์ ํ๋ฌ๊ทธ์ธ๊ณผ ์คํ ํ์ผ์ ์ปดํ์ผํ๋ ๋ฐ ํ์ํ CMakeLists.txt ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค.
cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR) FIND_PACKAGE( Boost 1.40 COMPONENTS system REQUIRED ) set (CMAKE_CXX_FLAGS "-g -Wall -std=c++11")
include (FindPkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(GAZEBO gazebo)
pkg_check_modules(SDF sdformat)
endif()
include_directories(
add_executable (request_publisher request_publisher.cc) target_link_libraries(request_publisher collision_map_creator_msgs ${GAZEBO_LIBRARIES} ${Boost_LIBRARIES} ${SDF_LIBRARIES}) add_dependencies(request_publisher collision_map_creator_msgs)
add_library(collision_map_creator SHARED collision_map_creator.cc ) target_link_libraries(collision_map_creator collision_map_creator_msgs ${Boost_LIBRARIES} ${GAZEBO_LIBRARIES} ${SDF_LIBRARIES}) add_dependencies(collision_map_creator collision_map_creator_msgs)
Boost๋ ํ๋ฌ๊ทธ์ธ์ ์ฝ๋ฐฑ ํจ์์์ ํ์ํฉ๋๋ค. ์ด๊ฒ์ ์ด๋ฏธ์ง ์์ฑ์์๊ฒ๋ ์ฌ์ฉ๋ฉ๋๋ค.
FIND_PACKAGE( Boost 1.40 COMPONENTS system REQUIRED )
msgs ๋๋ ํ ๋ฆฌ๋ ์คํ ํ์ผ๊ณผ ํ๋ฌ๊ทธ์ธ ๋ชจ๋์์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ include ๋๋ ํ ๋ฆฌ์ ์ถ๊ฐํด์ผํฉ๋๋ค.
include_directories(
GAZEBO_LIBRARY_DIRS ํ์ค ์ธ์๋ msgs ๋๋ ํ ๋ฆฌ์ ๋งํฌํด์ผํฉ๋๋ค. add_subdirectory ์ง์์ด๋ CMake์๊ฒ CMakeLists.txt ํ์ผ์ ์ฐพ๋๋ก ์ง์ํฉ๋๋ค. link_directories(${GAZEBO_LIBRARY_DIRS} ${SDF_LIBRARY_DIRS} ${CMAKE_CURRENT_BINARY_DIR}/msgs) add_subdirectory(msgs) ์คํ ํ์ผ request_publisher ๋ฅผ makefile์ ์ถ๊ฐํ์ญ์์ค. collision_map_creator_msgs , GAZEBO_LIBRARIES ๋ฐ Boost_LIBRARIES ์ ์ฐ๊ฒฐํด์ผํฉ๋๋ค. add_dependencies ์ง์์ด๋ CMake์๊ฒ ๋จผ์ collision_map_creator_msgs ๋ฅผ ๋น๋ add_dependencies ์ง์ํ๋ค.
add_executable (request_publisher request_publisher.cc) target_link_libraries(request_publisher collision_map_creator_msgs ${GAZEBO_LIBRARIES} ${Boost_LIBRARIES} ${SDF_LIBRARIES}) add_dependencies(request_publisher collision_map_creator_msgs)
์ด ํ๋ฌ๊ทธ์ธ์ ์ฐ๋ฆฌ ํ๋ฌ๊ทธ์ธ์ ๋ง๋ค๊ณ , ํ์ค WorldPlugin CMakeLists.txt์ ๋งค์ฐ ๋น์ทํฉ๋๋ค. ๋จ, collision_map_creator_msgs ์ ์ฐ๊ฒฐํ๊ณ ์ด๋ฅผ ์ข ์์ฑ์ผ๋ก ์ถ๊ฐํด์ผํฉ๋๋ค. add_library(collision_map_creator SHARED collision_map_creator.cc ) target_link_libraries(collision_map_creator collision_map_creator_msgs ${Boost_LIBRARIES} ${GAZEBO_LIBRARIES} ${SDF_LIBRARIES}) add_dependencies(collision_map_creator collision_map_creator_msgs)
๋จผ์ , protobuf-compiler๋ผ๋ ์ข ์ ํจํค์ง๋ฅผ ์ค์นํด์ผํฉ๋๋ค. sudo apt-get install protobuf-compiler
์ด ํ๋ก์ ํธ๋ฅผ ๋น๋ํ๋ ค๋ฉด ๋น๋ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค๊ณ CMake๋ฅผ ์คํ ํ ๋ค์ make๋ฅผ ์คํํ์ญ์์ค.
cd ~/collision_map_creator_plugin mkdir build cd build cmake ../ make ์ด ํ๋ฌ๊ทธ์ธ์ gazebo ํ๋ฌ๊ทธ์ธ ๊ฒฝ๋ก์ ์ถ๊ฐํด์ผํฉ๋๋ค. ํ๊ฒฝ ๋ณ์๋ฅผ ๋ณ๊ฒฝํ์ฌ์ด ๋น๋ ๋๋ ํ ๋ฆฌ๋ฅผ ์ฐธ์กฐํ๊ฑฐ๋,
export GAZEBO_PLUGIN_PATH=$GAZEBO_PLUGIN_PATH:collision_map_creator_plugin/build
๋๋ ํ๋ฌ๊ทธ์ธ์ ํ๋ฌ๊ทธ์ธ ๋๋ ํ ๋ฆฌ์ ๋ณต์ฌ ํ ์ ์์ต๋๋ค.
sudo cp libcollision_map_creator.so /usr/lib/gazebo-<YOUR-GAZEBO_VERSION>/plugins/
๋ชจ๋ ๊ฒ์ด ์๋์๋ค๊ณ ๊ฐ์ ํ๋ฉด, ์๋ง ๋๋ต ๊ฑฐ์น ๊ฐ์ ์ผ ๊ฒ์ ๋๋ค. (์ง์งํ๊ฒ๋ ์ด๊ฒ์ ๊ธธ์์ต๋๋ค), ์ฌ์ฉ์ ์ ์ wold ํ์ผ๋ก Gazebo๋ฅผ ์คํํด์ผํฉ๋๋ค.
model://ground_plane <include>
<uri>model://sun</uri>
</include>
<include>
<uri>model://willowgarage</uri>
</include>
<plugin filename="libcollision_map_creator.so" name="collision_map_creator"/>
</world>
Run Gazebo with this world:
gazebo ~/collision_map_creator_plugin/map_creator.world
~/collision_map_creator_plugin/build/request_publisher "(-10,10)(10,10)(10,-10)(-10,-10)" 10 0.01 ~/map.png
์คํ ๊ฐ๋ฅํ ํฐ๋ฏธ๋์ด Gazebo์ ์ฐ๊ฒฐ๋์ด ์๋์ง ํ์ธํ๊ณ ์์ฒญ ๋ฉ์์ง๋ฅผ ํ์ํด์ผํฉ๋๋ค. ์ ๋ง๋ ํฐ๋ฏธ๋์ ๋ฉ์์ง๊ฐ ํ์๋๊ณ ์๋ฃ ๋ ๋๊น์ง ํผ์ผํธ ์๋ฃ ํต๊ณ๊ฐ ํ์๋์ด์ผํฉ๋๋ค. map.png๋ ๋ค์๊ณผ ๊ฐ์ด ํ์๋์ด์ผํฉ๋๋ค.
ยฉ 2014 ์คํ ์์ค ๋ก๋ด ๊ณตํ ์ฌ๋จ
Gazebo๋ Apache 2.0์์ ๋ผ์ด์ผ์ค๊ฐ ๊ณต๊ฐ ๋ ์คํ ์์ค์ ๋๋ค. Google+ YouTube YouTube ~/collision_map_creator_plugin/build/request_publisher "(-10,10)(10,10)(10,-10)(-10,-10)" 10 0.01 ~/map.png
์คํ ๊ฐ๋ฅํ ํฐ๋ฏธ๋์ด Gazebo์ ์ฐ๊ฒฐ๋์ด ์๋์ง ํ์ธํ๊ณ ์์ฒญ ๋ฉ์์ง๋ฅผ ํ์ํด์ผํฉ๋๋ค. gazebo ํฐ๋ฏธ๋์ ๋ฉ์์ง๊ฐ ํ์๋๊ณ ์๋ฃ ๋ ๋๊น์ง ํผ์ผํธ ์๋ฃ ํต๊ณ๊ฐ ํ์๋์ด์ผํฉ๋๋ค.