Need some (expert) help with ROS2 C++ course project

Hi The Construct team,

I am currently working on the course project for ROS2 Basics in 5 Days with C++. The course project uses ROS2 Foxy but I learned with Galactic in the course.

Currently I am integrating my /find_wall Service Client and /record_odom Action Client with the base Wall Follower program - and I need help with the integration.

I tried to Google my question but could not find favorable results. I will have to Google further.
In the meantime, if you could help me out that would be great !

I am still new to the shared pointers and std::make_shared<...>() concepts in C++.

I have my C++ classes as shown below and I have my questions commented along. I have marked my questions inside as 1, 2, 3, 4, 5 and 6.

// #include(s)


class ServiceClient : public rclcpp::Node {
private:
  // private variables

public:
  // public variables

  ServiceClient() : Node("service_client_node") {

    // constructor statements

  }

  ~ServiceClient() {}

  bool is_service_done() const {
    // statements
  }

  void call_service() {
    // statements
  }

  void response_callback(rclcpp::Client<MyServiceMsg>::SharedFuture future) {
    // statements
  }
};

class ActionClient : public rclcpp::Node {
private:
  // private variables

public:
  // public variables

  explicit ActionClient(
      const rclcpp::NodeOptions &node_options = rclcpp::NodeOptions())
      : Node("action_client_node", node_options) {

    // constructor statements

  }

  ~ActionClient() {}

  bool is_goal_done() const {
    // statements
  }

  void send_goal() {
    // statements
  }

  void goal_response_callback(
      std::shared_future<MyActionMsgGoalHandle::SharedPtr> future) {
    // statements
  }

  void
  result_callback(const MyActionMsgGoalHandle::WrappedResult &action_result) {
    // statements
  }

  void feedback_callback(
      MyActionMsgGoalHandle::SharedPtr,
      const std::shared_ptr<const MyActionMsg::Feedback> action_feedback) {
    // statements
  }
};

class MyRobot : public rclcpp::Node {
private:
  // private variables

public:
  // public variables

  MyRobot() : Node("my_robot_node") {

    cmd_vel_pub = this->create_publisher<Twist>("/cmd_vel", 10);

    auto scan_sub_qos =
        rclcpp::QoS(rclcpp::KeepLast(10), rmw_qos_profile_sensor_data);
    rclcpp::SubscriptionOptions scan_sub_options;
    scan_sub_options.callback_group =
        create_callback_group(rclcpp::CallbackGroupType::Reentrant);
    scan_sub = this->create_subscription<LaserScan>(
        "/scan", scan_sub_qos,
        std::bind(&MyRobot::scan_callback, this,
                  std::placeholders::_1),
        scan_sub_options);

    auto odom_sub_qos =
        rclcpp::QoS(rclcpp::KeepLast(10), rmw_qos_profile_sensor_data);
    rclcpp::SubscriptionOptions odom_sub_options;
    odom_sub_options.callback_group =
        create_callback_group(rclcpp::CallbackGroupType::Reentrant);
    odom_sub = this->create_subscription<Odometry>(
        "/odom", odom_sub_qos,
        std::bind(&MyRobot::odom_callback, this,
                  std::placeholders::_1),
        odom_sub_options);

    timer_cbg = create_callback_group(rclcpp::CallbackGroupType::Reentrant);
    timer = this->create_wall_timer(
        std::chrono::milliseconds(1000),
        std::bind(&MyRobot::movement_callback, this), timer_cbg);

    // other statements

    // 1. How to initialize Service Client here so it can be used in movement_callback() method below ?

    // 2. How to initialize Action Client here so it can be used in movement_callback() method below ?
  }

  ~MyRobot() {}

  // other public methods

  void scan_callback(const LaserScan::SharedPtr scan_msg) {
    // statements
  }

  void odom_callback(const Odometry::SharedPtr odom_msg) {
    // statements
  }

  void movement_callback() {

    // service_client call_service() method is called here

    // 3. How to use rclcpp::spin_some(service_client) here ?

    
    // other statements for robot movement
    

    // action_client send_goal() method is called here

    // 4. How to use rclcpp::spin_some(action_client) here ?

    
    // other statements for robot movement

};

int main(int argc, char *argv[]) {
  rclcpp::init(argc, argv);
  auto robot_node = std::make_shared<MyRobot>();
  
  // 5. How to create service client node here ?
  // the service client must be initialized inside MyRobot class

  // 6. How to create action client node here ?
  // the action client must be initialized inside MyRobot class

  rclcpp::executors::MultiThreadedExecutor executor;
  executor.add_node(robot_node);

  executor.spin();

  RCLCPP_INFO(robot_node->get_logger(), "Terminating...");
  rclcpp::shutdown();
  return 0;
}

// End of Code

Thanks in advance,
Girish

Hi The Construct team,

I still have not found a way to get this working.
Could you please provide me any sample program code in C++ that shows how to use a Node inside another Node?
That would be very helpful

Thanks again.
Girish

@ The Construct team (and others who have the same problem),

I have finally solved this problem.

I actually got confused using the line
auto object = std::make_shared<NodeClassName>(),
and I declared a Node shared pointer as NodeClassName::SharedPtr
but later (after a lot of googling) I realized it must be
std::shared_ptr<NodeClassName> node_class_object = std::make_shared<NodeClassName>();

The auto keyword and ::SharedPtr caused a great confusion for me.

Now everything is alright. Understood my mistake through my own finding!

Thanks and Regards,
Girish

1 Like

Great that you solved it @girishkumar.kannan !

It might be worth giving a look to the Advanced Modern C++ course, which might help clarifying these points.

Hi @albertoezquerro ,

Thanks for the tip. I have that course on my course list… along with Intermediate ROS2, GTest for ROS2, Path Planning Basics and ROS1 & 2 Navigation.

– Girish