๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

12. [Option] C++ ์ฝ”๋“œ, template

๐Ÿง

12. [Option] C++ ์ฝ”๋“œ, template

์ด๋ฒˆ ๊ฐ•์˜, ๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ ๊ฐ•์˜์— ๊ฑธ์ณ์„œ ์•ž์„œ ๊ตฌํ˜„ํ–ˆ๋˜ ๋‹ค์–‘ํ•œ ROS ํ”„๋กœ๊ทธ๋žจ๋“ค์„ C++๋กœ ๋‹ค์‹œ ์ž‘์„ฑํ•ด๋ณด๋Š” ์‹œ๊ฐ„์„ ๊ฐ–๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฐ•์ขŒ๋ฅผ ๋”ฐ๋ผ์˜ด์— ์žˆ์–ด ๋ฌธ๋ฒ•์ ์ธ ์ธก๋ฉด์—์„ , C++ Class๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• ์ •๋„๋งŒ ์•Œ๊ณ  ๊ณ„์‹œ๋ฉด ๋”ฐ๋ผ์˜ค์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์™€ ๋”๋ถˆ์–ด ROS๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋นŒ๋“œ ํˆด๋กœ CMake๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ์— CMakeLists.txt์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ์žˆ๋‹ค๋ฉด ๋” ์ข‹์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ, ์‹œ์ž‘ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!!

basic_node.cpp


#include <ros/ros.h>

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

    ros::init(argc, argv, "basic_node");
    ros::NodeHandle nh;
    ros::Rate r(5); # Hz

    while ( ros::ok() ) {
        ROS_INFO("This is Basic Node");
        r.sleep();
    }

    return 0;
}
import rospy
rospy.init_node("drive_forward")
r = rospy.Rate(1)  # 1 Hz
r.sleep()
while not rospy.is_shutdown():
rospy.loginfo()

ros::Rate, r.sleep() ๋“ฑ ์ต์ˆ™ํ•œ ์ฝ”๋“œ๋“ค์ด ๋ณด์ด์ง€์š”?

node์˜ ์ƒ์„ฑ์€ ros::init์œผ๋กœ ๊ฐ€๋Šฅํ•˜๋ฉฐ, roscpp์—์„œ๋Š” NodeHandle์ด๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด์•ผ Publisher, Subscriber, serviceServer ๋“ฑ์˜ node๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก
NodeHandle์€ ๋ฐ˜๋“œ์‹œ ros::init ๋ณด๋‹ค ๋’ค์— ์ƒ์„ฑ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

ROS ํŒจํ‚ค์ง€ ๋นŒ๋“œํ•˜๊ธฐ


C++๋กœ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋Š” ๋นŒ๋“œ๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ caktin ์‹œ์Šคํ…œ์—์„œ๋Š” CMake๊ฐ€ ๋นŒ๋“œ๋ฅผ ๋„์™€์ค๋‹ˆ๋‹ค.

๋นŒ๋“œ ์‹œ์— ROS์™€ ๊ด€๋ จ๋œ ๊ณต์œ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ํ•„์š”ํ•  ๊ฒƒ์ž„์„ ๋‹ด๊ณ  ์žˆ๋Š” ๋ถ€๋ถ„์ด CMakeLists.txt ์ด๋ฉฐ ์ž์ฃผ ์ˆ˜์ •์ด ํ•„์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

CMake์— ๋Œ€ํ•ด์„œ๋Š” ์ž์„ธํžˆ ๋‹ค๋ฃจ์ง€ ์•Š๊ณ , ๊ธฐ์กด CMakeLists.txt ์™€ ์ฐจ์ด๊ฐ€ ์žˆ๋Š” ๋ถ€๋ถ„๋งŒ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  geometry_msgs
  sensor_msgs
)

๋ฐ”๋กœ find_package ํ‚ค์›Œ๋“œ๋กœ ROS์—์„œ ๊ธฐ๋ณธ ์ œ๊ณตํ•˜๋Š” ํŒจํ‚ค์ง€๋“ค์„ ์†์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

๐Ÿ’ก
๊ด€๋ จ๋œ ํŒจํ‚ค์ง€๋Š” ์ฃผ๋กœ ์ดˆ๊ธฐ ํŒจํ‚ค์ง€ ์ƒ์„ฑ ์‹œ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์†์‰ฝ๊ฒŒ import ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. catkin_create_pkg my_package <package1> roscpp

๋นŒ๋“œ ํ›„ ์ƒ์„ฑ๋œ ์‹คํ–‰ ํŒŒ์ผ ์ด๋ฆ„์„ ๊ฐ€์ง€๊ณ  launch file์ด๋‚˜ rosrun ์‹œ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒ ์ง€์š”?

์ด ์‹คํ–‰ ํŒŒ์ผ ์ด๋ฆ„์€ ๋‹ค์Œ ๋ถ€๋ถ„์—์„œ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
add_executable(basic_node src/basic_node.cpp)
add_executable(cmd_vel_pub_node src/cmdvel_pub.cpp)
add_executable(laser_sub_node src/laser_sub.cpp)
add_executable(parking_node src/parking.cpp)

...
add_dependencies(basic_node ${basic_node_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(cmd_vel_pub_node ${cmd_vel_pub_node_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(laser_sub_node ${laser_sub_node_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(parking_node ${parking_node_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

...
target_link_libraries(basic_node
  ${catkin_LIBRARIES}
)
target_link_libraries(cmd_vel_pub_node
  ${catkin_LIBRARIES}
)
target_link_libraries(laser_sub_node
  ${catkin_LIBRARIES}
)
target_link_libraries(parking_node
  ${catkin_LIBRARIES}
)

๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ!!! source devel/setup.bash


ํŒŒ์ด์ฌ๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ, roscpp์€ ์ฝ”๋“œ ์ˆ˜์ •์‹œ๋งˆ๋‹ค ๋นŒ๋“œ๋ฅผ ์ƒˆ๋กœ ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด์™€ ๋”๋ถˆ์–ด, ๋นŒ๋“œ ํ›„์—๋„ ๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ์„ ์—…๋ฐ์ดํŠธ ํ•ด์ค˜์•ผ ํ•˜๋ฉฐ ์ด๋Š” ๋‹ค์Œ ์ปค๋งจ๋“œ๋กœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

(build done...)

$ cd ~/gcamp_ws
$ source devel/setup.bash
# ๋˜๋Š”
$ sds

Tip!! ํ•˜๋‚˜์˜ ํŒจํ‚ค์ง€๋งŒ ๋นŒ๋“œํ•˜๊ธฐ


๊ธฐ๋ณธ์ ์œผ๋กœ catkin_make๋ฅผ ํ•˜๊ฒŒ๋˜๋ฉด workspace์•ˆ์— ์žˆ๋Š” ๋ชจ๋“  ํŒจํ‚ค์ง€๋“ค์„ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ์ € ๋˜ํ•œ ์ด ๋ฐฉ๋ฒ•์ด ํƒํƒ์น˜ ์•Š์•„ ์ž‘์—…์ค‘์ธ ํ•˜๋‚˜์˜ ํŒจํ‚ค์ง€๋งŒ ๋นŒ๋“œํ•˜๊ณ ์ž ๋‹จ์ถ•ํ‚ค๋ฅผ ์„ค์ •ํ•˜์˜€๋Š”๋ฐ์š”...

๐Ÿ’ก
์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ฐ•์˜๋ฅผ ํ™•์ธํ•˜์„ธ์š”!! ๐Ÿ˜‰๐Ÿ˜‰

๊ทธ๋Ÿผ, topic publisher, subscriber ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

cmdvel_pub.cpp


/*
 * basic topic publisher example
 * 
 * referenced from wiki.ros.org : http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29
 */

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

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

    ros::init(argc, argv, "cmd_vel_pub_node");
    ros::NodeHandle nh;
    ROS_INFO("==== DriveForward node Started, move forward during 10 seconds ====\n");

    ros::Publisher cmd_pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel", 5);
    ros::Rate r(5);

    geometry_msgs::Twist go_forward;
    go_forward.linear.x = 0.5;

    geometry_msgs::Twist stop;
    stop.linear.x = 0.0;

    auto start = std::chrono::steady_clock::now();
    auto now = std::chrono::steady_clock::now();

    std::chrono::duration<double> time_duration = now - start;


    while (time_duration.count() < 5.0){
        cmd_pub.publish(go_forward);
        
        now = std::chrono::steady_clock::now();
        time_duration = now - start;
    }
    
    ROS_WARN(" 5 seconds passed, Stop!! \n");
    cmd_pub.publish(stop);

    return 0;
}

  • ์‚ฌ์‹ค์ƒ ์ฃผ์˜๊นŠ๊ฒŒ ๋ณผ ๋ถ€๋ถ„์€ ๋‹ค์Œ ํ•œ์ค„์ž…๋‹ˆ๋‹ค.
ros::Publisher cmd_pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel", 5);

# python
pub = rospy.Publisher("/cmd_vel", Twist, queue_size=1)

Publisher ์ƒ์„ฑ ์‹œ, ์ ํ•ฉํ•œ message๋ฅผ ๋ช…๊ธฐํ•˜์—ฌ instantiation์„ ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋”๋ถˆ์–ด, ๋‘๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ํŒŒ์ด์ฌ ๋•Œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ queue size์ž…๋‹ˆ๋‹ค.

์ด์™€ ๋”๋ถˆ์–ด, message๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐฉ์‹์ด ๋‹ค๋ฅด์ง€์š”?

#include <geometry_msgs/Twist.h>

...
    ros::Publisher cmd_pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel", 5);
...

laser_sub.cpp


// basic topic subscriber example
// referenced from wiki.ros.org : http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c%2B%2B%29

#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>

void sub_callback(const sensor_msgs::LaserScan &data ){
    std::cout << "Front Lidar Value : " << data.ranges[360] << std::endl;
}

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

    ros::init(argc, argv, "laser_sub_node");
    ros::NodeHandle nh;    
    ros::Subscriber sub = nh.subscribe("/scan", 1, sub_callback);

    ros::spin();

    return 0;
}

template<class M>
ros::Subscriber subscribe(const std::string& topic, uint32_t queue_size,
		 <callback, which may involve multiple arguments>,
		 const ros::TransportHints& transport_hints = ros::TransportHints());

  • publisher์™€ ๋‹ฌ๋ฆฌ, instantiation์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์— ์œ ์˜ํ•ฉ๋‹ˆ๋‹ค.
ros::Publisher cmd_pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel", 5);
ros::Subscriber sub = nh.subscribe("/scan", 1, sub_callback);

  • ์ €์˜ ๊ฒฝ์šฐ ๋ ˆํผ๋Ÿฐ์Šค๋กœ ์ ˆ๋‹ฌํ–ˆ์ง€๋งŒ, ROS์—์„œ๋Š” boost::shared_ptr ํ˜•์‹์˜ ํƒ€์ž…๋“ค์„ ๊ธฐ๋ณธ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ต๋‹ˆ๋‹ค. (์ด ๋‚ด์šฉ์€ ์ดํ•ด๊ฐ€ ์•ˆ๋˜๋ฉด ๋„˜์–ด๊ฐ€์…”๋„ ์ข‹์Šต๋‹ˆ๋‹ค. ์ €๋„ ์ž˜ ๋ชจ๋ฅธ๋‹ต๋‹ˆ๋‹ค ใ…Žใ…Ž)
void sub_callback(const sensor_msgs::LaserScan &data ){
    std::cout << "Front Lidar Value : " << data.ranges[360] << std::endl;
}

# ์ด๊ฒƒ๋„ ๊ฐ€๋Šฅ!
void sub_callback(const sensor_msgs::LaserScanConstPtr &data ){
    std::cout << "Front Lidar Value : " << data.ranges[360] << std::endl;
}

๐Ÿ’ก
iostream์ด ์—†๋Š”๋ฐ cout์ด ์žˆ๋„ค์š”?
#include <ros/ros.h>

์ด ๋ถ€๋ถ„์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ—ค๋” ํŒŒ์ผ๋“ค์ด

include ๋œ๋‹ต๋‹ˆ๋‹ค ๐Ÿ™ƒ๐Ÿ™ƒ

  • iostream
  • string
  • boost
  • sstream
  • vector
  • map
  • assert.h

    ...

์•ž์„  ๋‘ ์˜ˆ์ œ๋“ค์„ ํ•ฉํ•œ ๊ฒƒ์ด parking ์˜ˆ์ œ์˜€์ง€์š”? ๋ฐ”๋กœ ์‚ดํŽด๋ด…์‹œ๋‹ค!

parking.cpp


// Park your robot with lidar distance value
// 
// created by kimsooyoung : https://github.com/kimsooyoung

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

class TinyBot
{
private:
    ros::NodeHandle m_nh;
    ros::Publisher m_pub;
    ros::Subscriber m_sub;

    std::string m_name;
    geometry_msgs::Twist m_cmd_vel;

public:
    TinyBot(const std::string &name_in = "my_tiny"): m_name(name_in) {
        ROS_INFO("Publisher and Subscriber initialized");
        m_pub = m_nh.advertise<geometry_msgs::Twist>("/cmd_vel", 5);
        m_sub = m_nh.subscribe("/scan", 1, &TinyBot::subCallback, this);
    }

    void robotStop(){
        m_cmd_vel.linear.x = 0.0;
        m_pub.publish(m_cmd_vel);
        ROS_INFO(" Parking Done!!"); 
        ros::shutdown();
    }

    void robotGo(){
        m_cmd_vel.linear.x = 0.8;
        m_pub.publish(m_cmd_vel);
    }

    void subCallback(const sensor_msgs::LaserScan &data){

        auto frontLidarPoint = data.ranges[360];

        if ( frontLidarPoint >= 1.0 ){
            this->robotGo();
        }
        else {
            this->robotStop();
        }

        std::cout << "===== front lidar point val : " << frontLidarPoint << " ====" << std::endl;
    }
};

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

    ros::init(argv, argc, "parking_node");
    TinyBot tinybot("gcamp_robo");
    ros::spin();

    return 0;
}

๋ฐ”๋กœ ํด๋ž˜์Šค๊ฐ€ ๋“ฑ์žฅํ•ด์„œ ์กฐ๊ธˆ ๋‹นํ™ฉํ•˜์…จ์„ ์ˆ˜๋„ ์žˆ์„ํ…๋ฐ์š”, ์ฒœ์ฒœํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  • ๊ธฐ๋Šฅ ๋ถ€๋ถ„์€ ์‚ฌ์‹ค์ƒ ํŒŒ์ด์ฌ ์˜ˆ์ œ์—์„œ์˜ ๋กœ์ง๊ณผ ์ฐจ์ด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
    void robotStop(){
        m_cmd_vel.linear.x = 0.0;
        m_pub.publish(m_cmd_vel);
        ROS_INFO(" Parking Done!!"); 
        ros::shutdown();
    }

    void robotGo(){
        m_cmd_vel.linear.x = 0.8;
        m_pub.publish(m_cmd_vel);
    }

    void subCallback(const sensor_msgs::LaserScan &data){

        auto frontLidarPoint = data.ranges[360];

        if ( frontLidarPoint >= 1.0 ){
            this->robotGo();
        }
        else {
            this->robotStop();
        }

        std::cout << "===== front lidar point val : " << frontLidarPoint << " ====" << std::endl;
    }

ros::shutdown(); ์„ ํ†ตํ•ด ๋…ธ๋“œ ์ข…๋ฃŒ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ต๋‹ˆ๋‹ค.

  • ์ƒ์„ฑ์ž ๋ถ€๋ถ„์ด ๋” ์ƒ์†Œํ•˜์‹ค ๊ฒƒ ๊ฐ™์€๋ฐ์š”, ๋ ˆํผ๋Ÿฐ์Šค์˜ ์„ ์–ธ ๋ถ€๋ถ„์„ ์ฒจ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.
public:
    TinyBot(const std::string &name_in = "my_tiny"): m_name(name_in) {
        ROS_INFO("Publisher and Subscriber initialized");
        m_pub = m_nh.advertise<geometry_msgs::Twist>("/cmd_vel", 5);
        m_sub = m_nh.subscribe("/scan", 1, &TinyBot::subCallback, this);
    }

ํด๋ž˜์Šค method๋กœ callback์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด function pointer์™€ object๋ฅผ ๋ฐ›๋Š” ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด ๋˜ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ์ดํ•ดํ•˜๋ ค ํ•˜์‹œ๊ธฐ๋ณด๋‹ค, ํด๋ž˜์Šค method๋ฅผ callback์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ ํ”„๋ฉด ์ด๋ ‡๊ฒŒ ์“ฐ๋‚˜๋ณด๋‹ค, ํ•˜๊ณ  ์ดํ•ดํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

roscpp: ros::NodeHandle Class Reference
roscpp's interface for creating subscribers, publishers, etc. This class is used for writing nodes. It provides a RAII interface to this process' node, in that when the first NodeHandle is created, it instantiates everything necessary for this node, and when the last NodeHandle goes out of scope it shuts down the node.
http://docs.ros.org/en/kinetic/api/roscpp/html/classros_1_1NodeHandle.html#a317fe4c05919e0bf3fb5162ccb2f7c28

์—ฌ๊ธฐ์„œ, ํด๋ž˜์Šค๋กœ ํ•˜์ง€ ์•Š๊ณ  ์ „์—ญ๋ณ€์ˆ˜๋กœ ์„ค์ •ํ•˜๋ฉด ๋˜์ง€ ์•Š๋‚˜? ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“œ์‹ค ์ˆ˜ ์žˆ๋Š”๋ฐ์š”. ์•ž์„œ ๋ฐฐ์šด ๋‚ด์šฉ๋“ค์„ ์ข…ํ•ฉํ•˜์—ฌ ์ƒ๊ฐํ•ด๋ณด๋ฉด

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

void sub_callback(const sensor_msgs::LaserScan &data ){

    // do Something
    geometry_msgs::Twist pub_msg;

    cmd_pub.publish(pub_msg); // ....?
}

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

    ros::init(argv, argc, "parking_node");
    ros::NodeHandle nh;

    ros::Publisher cmd_pub = nh.advertise<geometry_msgs::Twist>("/cmd_vel", 5);
    ros::Subscriber scan_sub = nh.subscribe("/scan", 1, subCallback);

    ros::spin();

    return 0;
}
  • subscriber๋Š” NodeHandle์ด ์žˆ์–ด์•ผ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • NodeHandle์€ init์ด ์žˆ์–ด์•ผ ์ƒ์„ฑ ๊ฐ€๋Šฅํ•˜์ง€์š”.

โ‡’ ๊ทธ๋Ÿผ ๊ฒฐ๊ตญ ๋ชจ๋“  ์ดˆ๊ธฐํ™” ๋ถ€๋ถ„์ด main ๋ฐ–์— ์กด์žฌํ•˜๊ฒŒ ๋˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค!! ๐Ÿ‘น๐Ÿ‘น

์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ROS ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ์ „์—ญ๋ณ€์ˆ˜๋ฅผ ์ง€์–‘ํ•˜๊ณ  ์žˆ๋‹ต๋‹ˆ๋‹ค ๐Ÿ˜‰

Globals


Globals, both variables and functions, are discouraged. They pollute the namespace and make code less reusable.

Global variables, in particular, are strongly discouraged. They prevent multiple instantiations of a piece of code and make multi-threaded programming a nightmare.

Most variables and functions should be declared inside classes. The remainder should be declared inside namespaces.

Exception: a file may contain aย main()ย function and a handful of small helper functions that are global. But keep in mind that one day those helper function may become useful to someone else.

Wiki
This page defines a style guide to be followed in writing C++ code for ROS. This guide applies to all ROS code, both core and non-core. For Python, see the PyStyleGuide and for Javascript, see the ROS JavaScript Style Guide Coding style is important.
http://wiki.ros.org/CppStyleGuide

๋‹ค์Œ ์‹œ๊ฐ„์— ์ด์–ด์„œ, Service, Action ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!!