0
$\begingroup$

I just started out with ROS 2.0. I am unsure if I am doing something stupid or missing out something. However, I can’t build these files. If I should have posted somewhere else kindly let me know. i'm struggle with it already for a week, I hope someone can solve it for me, thank you very much

The command used for building: colcon build

Error thrown:
/usr/bin/ld: CMakeFiles/sign_detection.dir/src/sign_detection.cpp.o: in function `main':
sign_detection.cpp:(.text+0x27b): undefined reference to `rclcpp::shutdown(std::shared_ptr<rclcpp::Context>, std::string const&)'
/usr/bin/ld: CMakeFiles/sign_detection.dir/src/sign_detection.cpp.o: in function `rclcpp::ParameterTypeException::ParameterTypeException(rclcpp::ParameterType, rclcpp::ParameterType)':
sign_detection.cpp:(.text._ZN6rclcpp22ParameterTypeExceptionC2ENS_13ParameterTypeES1_[_ZN6rclcpp22ParameterTypeExceptionC5ENS_13ParameterTypeES1_]+0x3e): undefined reference to `rclcpp::to_string(rclcpp::ParameterType)'
/usr/bin/ld: sign_detection.cpp:(.text._ZN6rclcpp22ParameterTypeExceptionC2ENS_13ParameterTypeES1_[_ZN6rclcpp22ParameterTypeExceptionC5ENS_13ParameterTypeES1_]+0x50): undefined reference to `rclcpp::to_string(rclcpp::ParameterType)'
/usr/bin/ld: CMakeFiles/sign_detection.dir/src/sign_detection.cpp.o: in function `auto rclcpp::Node::declare_parameter<double>(std::string const&, double const&, rcl_interfaces::msg::ParameterDescriptor_<std::allocator<void> > const&, bool)':
sign_detection.cpp:(.text._ZN6rclcpp4Node17declare_parameterIdEEDaRKSsRKT_RKN14rcl_interfaces3msg20ParameterDescriptor_ISaIvEEEb[_ZN6rclcpp4Node17declare_parameterIdEEDaRKSsRKT_RKN14rcl_interfaces3msg20ParameterDescriptor_ISaIvEEEb]+0x8e): undefined reference to `rclcpp::Node::declare_parameter(std::string const&, rclcpp::ParameterValue const&, rcl_interfaces::msg::ParameterDescriptor_<std::allocator<void> > const&, bool)'
/usr/bin/ld: CMakeFiles/sign_detection.dir/src/sign_detection_node.cpp.o: in function `SignDetectionNode::SignDetectionNode()':
sign_detection_node.cpp:(.text+0x346): undefined reference to `libpsaf::SignDetectionInterface::SignDetectionInterface(std::string, unsigned int, std::vector<std::string, std::allocator<std::string> >, std::string, std::string, std::string, rclcpp::QoS)'
/usr/bin/ld: CMakeFiles/sign_detection.dir/src/sign_detection_node.cpp.o: in function `SignDetectionNode::publishImageWithBoundin

this is sign_detection_node.cpp

/**
 * @file sign_detection_node.cpp
 * @brief the implementation of the SignDetectionNode class
 * @author PSAF
 * @date 2022-06-01
 */
#include "psaf_sign_detection/sign_detection_node.hpp"
#include <vector>
#include <string>
#include <tuple>
#include <algorithm>
#include "rclcpp/rclcpp.hpp"

SignDetectionNode::SignDetectionNode()
: SignDetectionInterface(
    SIGN_DETECTION_NODE_NAME,
    NBR_OF_CAMS_RGB,
    CAM_TOPIC_RGB,
    STATE_TOPIC,
    SIGN_TOPIC,
    STATUS_INFO_TOPIC,
    rclcpp::QoS(rclcpp::KeepLast {10})
){
   // load model
  try {
    module_ = torch::jit::load("/home/tuodou/wise-2023-24-ctrl-alt-defeat-feature-lane-detection/src/psaf_sign_detection/models/yolov5n.torchscript");
  } catch (const c10::Error & e) {
    std::cerr << "error loading the model\n";
  }
  std::cout << "Model Loading OK\n";
  module_.eval();

  // load sign names
  std::ifstream infile("/home/tuodou/wise-2023-24-ctrl-alt-defeat-feature-lane-detection/src/psaf_sign_detection/models/sign.names");
  if (infile.is_open()) {
    std::string line;
    while (getline(infile, line)) {
      class_names_.emplace_back(line);
    }
    infile.close();
  } else {
    std::cerr << "Error loading the class names!\n";
  }

  // set parameters
  conf_threshold_ = 0.4;
  iou_threshold_ = 0.5;
}


void SignDetectionNode::processImage(cv::Mat & img, int sensor)
{
  // save time and current speed
  // auto start = std::chrono::high_resolution_clock::now();
  // double speed = current_speed_;
  cv::Size original_size = img.size();

// #ifdef DEBUG_IMAGE
//   // create a clone of the image to not change the original image
//   cv::Mat img_clone = img.clone();
// #endif

  // resize image to have the wanted size with boundaries
  std::vector<float> pad_info = getLetterboxImage(img, img, cv::Size(640, 640));
  const float pad_w = pad_info[0];
  const float pad_h = pad_info[1];
  const float scale = pad_info[2];

  // normalization 1/255
  img.convertTo(img, CV_32FC3, 1.0f / 255.0f);
  // convert image to tensor
  auto tensor_img = torch::from_blob(img.data, {1, img.rows, img.cols, img.channels()});
  tensor_img = tensor_img.permute({0, 3, 1, 2}).contiguous();

  // create input vector
  std::vector<torch::jit::IValue> inputs;
  inputs.emplace_back(tensor_img);

  // perform forward pass through yolo network
  torch::jit::IValue output = module_.forward(inputs);

  // post process the detections
  torch::Tensor detections = output.toTuple()->elements()[0].toTensor();
  std::vector<Detection> result = postProcessing(
    detections, pad_w, pad_h, scale, original_size,
    conf_threshold_, iou_threshold_);
  // auto end = std::chrono::high_resolution_clock::now();
  // auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

  // iterate through all detections
  // for (const Detection & sign_detect : result) {
  //   // create a sign message
  //   publishSignMessage(sign_detect, duration.count(), speed);
  // }

// #ifdef DEBUG_IMAGE
//   publishImageWithBoundingBox(img_clone, result, class_names_);
// #endif
}
int SignDetectionNode::mapDetToSignType(int detection)
{
  // determine correct sign type
  int out_sign;
  switch (detection) {
    case 0:
      std::cout << "STOP" << std::endl;
      out_sign = STOP_SIGN;
      break;
    case 1:
      std::cout << "PEDESTRIAN" << std::endl;
      out_sign = PEDESTRIAN_SIGN;
      break;
    case 2:
      std::cout << "YIELD" << std::endl;
      out_sign = YIELD_SIGN;
      break;
    case 3:
      std::cout << "ZONE_30" << std::endl;
      out_sign = LIMIT_30_SIGN;
      break;
    case 4:
      std::cout << "END_ZONE_30" << std::endl;
      out_sign = END_LIMIT_30_SIGN;
      break;
    default:
      std::cout << "Such a sign is not defined" << std::endl;
      break;
  }
  return out_sign;
}
void SignDetectionNode::updateState(std_msgs::msg::Int64::SharedPtr state)
{
  (void) state;
}

void SignDetectionNode::update()
{

}

void SignDetectionNode::publishImageWithBoundingBox(
  cv::Mat & img,
  const std::vector<Detection> & detections,
  const std::vector<std::string> & class_names,
  bool label)
{
  // only if something was detected
  if (!detections.empty()) {
    // iterate through all detections
    for (const auto & detection : detections) {
      const auto & box = detection.bbox;
      float score = detection.score;
      int class_idx = detection.class_idx;

      // draw rectangle
      cv::rectangle(img, box, cv::Scalar(0, 0, 255), 2);

      // draw label
      if (label) {
        std::stringstream ss;
        ss << std::fixed << std::setprecision(2) << score;
        std::string s = class_names[class_idx] + " " + ss.str();

        auto font_face = cv::FONT_HERSHEY_DUPLEX;
        auto font_scale = 1.0;
        int thickness = 1;
        int baseline = 0;
        auto s_size = cv::getTextSize(s, font_face, font_scale, thickness, &baseline);
        cv::rectangle(
          img,
          cv::Point(box.tl().x, box.tl().y - s_size.height - 5),
          cv::Point(box.tl().x + s_size.width, box.tl().y),
          cv::Scalar(0, 0, 255), -1);
        cv::putText(
          img, s, cv::Point(box.tl().x, box.tl().y - 5),
          font_face, font_scale, cv::Scalar(255, 255, 255), thickness);
      }
    }
  }

  // publish image
  // this->publishImage(img, "camera_link");
}

std::vector<Detection> SignDetectionNode::postProcessing(
  const torch::Tensor & detections,
  float pad_w, float pad_h, float scale, const cv::Size & img_shape,
  float conf_thres, float iou_thres)
{
  constexpr int item_attr_size = 5;
  // number of classes, e.g. 5 for our dataset
  auto num_classes = detections.size(2) - item_attr_size;

  // get candidates which object confidence > threshold
  auto conf_mask = detections.select(2, 4).ge(conf_thres).unsqueeze(2);

  // apply constrains to get filtered detections for current image
  auto det = torch::masked_select(detections[0], conf_mask[0]).view(
    {-1, num_classes + item_attr_size});

  // declare detection vector
  std::vector<Detection> det_vec;

  // only do processing if a sign was detected
  if (det.size(0) != 0) {
    // compute overall score = obj_conf * cls_conf, similar to x[:, 5:] *= x[:, 4:5]
    det.slice(1, item_attr_size, item_attr_size + num_classes) *= det.select(1, 4).unsqueeze(1);

    // box (center x, center y, width, height) to (x1, y1, x2, y2)
    torch::Tensor box = xywh2xyxy(det.slice(1, 0, 4));

    // [best class only] get the max classes score at each result (e.g. elements 5-84)
    std::tuple<torch::Tensor, torch::Tensor> max_classes =
      torch::max(det.slice(1, item_attr_size, item_attr_size + num_classes), 1);

    // class score and index
    auto max_conf_score = std::get<0>(max_classes);
    auto max_conf_index = std::get<1>(max_classes);

    max_conf_score = max_conf_score.to(torch::kFloat).unsqueeze(1);
    max_conf_index = max_conf_index.to(torch::kFloat).unsqueeze(1);

    // shape: n * 6, top-left x/y (0,1), bottom-right x/y (2,3), score(4), class index(5)
    det = torch::cat({box.slice(1, 0, 4), max_conf_score, max_conf_index}, 1);

    // for batched NMS
    constexpr int max_wh = 4096;
    auto c = det.slice(1, item_attr_size, item_attr_size + 1) * max_wh;
    auto offset_box = det.slice(1, 0, 4) + c;

    // define vectors
    std::vector<cv::Rect> offset_box_vec;
    std::vector<float> score_vec;
    const auto & det_array = det.accessor<float, 2>();

    // use accessor to access tensor elements efficiently
    tensor2Detection(
      offset_box.accessor<float, 2>(), det_array, offset_box_vec, score_vec);

    // run non maximum suppression
    std::vector<int> nms_indices;
    cv::dnn::NMSBoxes(offset_box_vec, score_vec, conf_thres, iou_thres, nms_indices);

    // iterate through all remaining detections
    for (int index : nms_indices) {
      Detection t;
      const auto & b = det_array[index];
      t.bbox = cv::Rect(
        cv::Point(b[Det::tl_x], b[Det::tl_y]),
        cv::Point(b[Det::br_x], b[Det::br_y]));
      t.score = det_array[index][Det::score];
      t.class_idx = det_array[index][Det::class_idx];
      det_vec.emplace_back(t);
    }

    // scale coordinates to original image size
    scaleCoordinates(det_vec, pad_w, pad_h, scale, img_shape);
  }

  return det_vec;
}

void SignDetectionNode::scaleCoordinates(
  std::vector<Detection> & data, float pad_w, float pad_h,
  float scale, const cv::Size & img_shape)
{
  // define lambda function for clipping
  auto clip = [](float n, float lower, float upper) {
      return std::max(lower, std::min(n, upper));
    };

  std::vector<Detection> detections;
  // iterate through all detections in data
  for (auto & i : data) {
    // calculate scaled coordinates
    float x1 = (i.bbox.tl().x - pad_w) / scale;  // x padding
    float y1 = (i.bbox.tl().y - pad_h) / scale;  // y padding
    float x2 = (i.bbox.br().x - pad_w) / scale;  // x padding
    float y2 = (i.bbox.br().y - pad_h) / scale;  // y padding

    // apply clipping
    x1 = clip(x1, 0, img_shape.width);
    y1 = clip(y1, 0, img_shape.height);
    x2 = clip(x2, 0, img_shape.width);
    y2 = clip(y2, 0, img_shape.height);

    // create bounding box
    i.bbox = cv::Rect(cv::Point(x1, y1), cv::Point(x2, y2));
  }
}

torch::Tensor SignDetectionNode::xywh2xyxy(const torch::Tensor & x)
{
  auto y = torch::zeros_like(x);
  // convert bounding box format from (center x, center y, width, height) to (x1, y1, x2, y2)
  y.select(1, Det::tl_x) = x.select(1, 0) - x.select(1, 2).div(2);
  y.select(1, Det::tl_y) = x.select(1, 1) - x.select(1, 3).div(2);
  y.select(1, Det::br_x) = x.select(1, 0) + x.select(1, 2).div(2);
  y.select(1, Det::br_y) = x.select(1, 1) + x.select(1, 3).div(2);
  return y;
}

void SignDetectionNode::tensor2Detection(
  const at::TensorAccessor<float, 2> & offset_boxes,
  const at::TensorAccessor<float, 2> & det,
  std::vector<cv::Rect> & offset_box_vec,
  std::vector<float> & score_vec)
{
  // iterate all tensor boxes
  for (int i = 0; i < offset_boxes.size(0); i++) {
    offset_box_vec.emplace_back(
      cv::Rect(
        cv::Point(offset_boxes[i][Det::tl_x], offset_boxes[i][Det::tl_y]),
        cv::Point(offset_boxes[i][Det::br_x], offset_boxes[i][Det::br_y]))
    );
    score_vec.emplace_back(det[i][Det::score]);
  }
}

std::vector<float> SignDetectionNode::getLetterboxImage(
  const cv::Mat & src, cv::Mat & dst,
  const cv::Size & out_size)
{
  auto in_h = static_cast<float>(src.rows);
  auto in_w = static_cast<float>(src.cols);
  float out_h = out_size.height;
  float out_w = out_size.width;

  // find scaling factor
  float scale = std::min(out_w / in_w, out_h / in_h);

  // calculate middle points
  int mid_h = static_cast<int>(in_h * scale);
  int mid_w = static_cast<int>(in_w * scale);

  // resize image to wanted size
  cv::resize(src, dst, cv::Size(mid_w, mid_h));

  // calculate boundaries of letterbox
  int top = (static_cast<int>(out_h) - mid_h) / 2;
  int down = (static_cast<int>(out_h) - mid_h + 1) / 2;
  int left = (static_cast<int>(out_w) - mid_w) / 2;
  int right = (static_cast<int>(out_w) - mid_w + 1) / 2;

  // create letterbox image
  cv::copyMakeBorder(
    dst, dst, top, down, left, right, cv::BORDER_CONSTANT,
    cv::Scalar(114, 114, 114));

  // return left and top boundary and scale
  std::vector<float> pad_info{static_cast<float>(left), static_cast<float>(top), scale};
  return pad_info;
}

this is sign_detection.cpp

/**
 * @file sign_detection.cpp
 * @brief the main method for the sign detection. This function gets called by the launch file
 * @author PSAF
 * @date 2022-06-01
 */
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "psaf_sign_detection/sign_detection_node.hpp"

/**
* Main: Start the Sign Detection Node
*/

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  std::shared_ptr<SignDetectionNode> node = std::make_shared<SignDetectionNode>();
  rclcpp::WallRate rate(node->declare_parameter("update_frequency", rclcpp::PARAMETER_DOUBLE)
    .get<rclcpp::ParameterType::PARAMETER_DOUBLE>());
  while (rclcpp::ok()) {
    rclcpp::spin_some(node);
    node->update();
    rate.sleep();
  }

  rclcpp::shutdown();
}

hpp is here:

/**
 * @file sign_detection_node.hpp
 * @brief The definition of the SignDetectionNode class
 * @author PSAF
 * @date 2022-06-01
 */
#ifndef PSAF_SIGN_DETECTION__SIGN_DETECTION_NODE_HPP_
#define PSAF_SIGN_DETECTION__SIGN_DETECTION_NODE_HPP_

#include <string>
#include <vector>

#include "rclcpp/rclcpp.hpp"
#include "opencv4/opencv2/opencv.hpp"
#include "libpsaf/interface/sign_detection_interface.hpp"
#include "psaf_configuration/configuration.hpp"
#include "psaf_sign_detection/definitions.hpp"
#include "libpsaf_msgs/msg/sign.hpp"
#include "std_msgs/msg/float64.hpp"
#include "torch/script.h"
#include "torch/torch.h"


/**
 * @class SignDetectionNode
 * @implements SignDetectionInterface
 * @brief Detect the signs located next to the road.
 * @details This class is the node of the sign detection. It is responsible for
 *          the detection of signs and the publishing of the detected signs.
 */
class SignDetectionNode : public libpsaf::SignDetectionInterface
{
public:
  SignDetectionNode();

  /**
   * @brief Method in which the results get published
   * @details This method is called periodically by the main method of the node.
   */
  void update();

protected:
  /**
   * @brief Callback Method for the image topic
   * @param[in] img the image
   * @param[in] sensor the position of the topic in the topic vector
   */
  void processImage(cv::Mat & img, int sensor) final;

  /**
   * @brief Callback Method for the state
   * @param[in] state the current state of the state machine
   */
  void updateState(std_msgs::msg::Int64::SharedPtr state) override;

  /**
  * @brief callback function for speed
  * @param p
  */
  void updateSpeed(std_msgs::msg::Float64::SharedPtr p);

  /**
   * @brief create and publish a sign message
   * @param sign_detect Detection struct of detected sign
   * @param time time it took for the processing in milliseconds
   * @param speed speed of the car at the beginning of processing
   */
  void publishSignMessage(const Detection & sign_detect, double time, double speed);

  /**
   * @brief map the sign type to the numbers wanted in the state machine
   * @param detection index of detected sign in network
   * @return index of detected sign for state machine
   */
  static int mapDetToSignType(int detection);

  /**
   * @brief calculate the area of the bounding box
   * @param height height of bounding box
   * @param width width of bounding box
   * @return area
   */
  static double getBoundingBoxArea(int height, int width);

  /**
   * @brief determine distance from sign in y direction given the area of the bounding box
   * @param area area of the bounding box
   * @return distance in y direction in mm
   */
  static double getSignDistance(double area);

  /**
   * @brief resize a given image to a wanted size
   * @param src source image
   * @param dst destination image
   * @param out_size desired output size
   * @return padding information
   */
  static std::vector<float> getLetterboxImage(
    const cv::Mat & src, cv::Mat & dst, const
    cv::Size & out_size);

  /**
   * @brief draw detected bounding boxes into the image and publish it
   * @param img image
   * @param detections detected signs
   * @param class_names file with all class names
   * @param label true if label should be printed
   */
  void publishImageWithBoundingBox(
    cv::Mat & img,
    const std::vector<Detection> & detections,
    const std::vector<std::string> & class_names,
    bool label = true);

  /**
   * @brief preprocess the output of the neural network
   * @param detections detection tensor
   * @param pad_w amount of padding in width direction
   * @param pad_h amount of padding in height direction
   * @param scale factor with which image was scaled
   * @param img_shape original image shape
   * @param conf_thres confidence threshold
   * @param iou_thres threshold for IoU
   * @return vector with all detections
   */
  static std::vector<Detection> postProcessing(
    const torch::Tensor & detections,
    float pad_w, float pad_h, float scale, const cv::Size & img_shape,
    float conf_thres = 0.4, float iou_thres = 0.5);

  /**
   * @brief scales the detected bounding boxes to fit to the original image size
   * @param data detections
   * @param pad_w amount of padding in width direction
   * @param pad_h amount of padding in height direction
   * @param scale factor with which image was scaled
   * @param img_shape img_shape original image shape
   */
  static void scaleCoordinates(
    std::vector<Detection> & data, float pad_w, float pad_h,
    float scale, const cv::Size & img_shape);

  /**
   * @brief convert bounding box format
   * @param x tensor with indices
   * @return new indices
   */
  static torch::Tensor xywh2xyxy(const torch::Tensor & x);

  /**
   * @brief convert detections from tensor to Detection struct
   * @param offset_boxes offset of the boxes
   * @param det detections
   * @param offset_box_vec offset box vector
   * @param score_vec score vector
   */
  static void tensor2Detection(
    const at::TensorAccessor<float, 2> & offset_boxes,
    const at::TensorAccessor<float, 2> & det,
    std::vector<cv::Rect> & offset_box_vec,
    std::vector<float> & score_vec);

  // /**
  //  * @brief callback for state, not implemented
  //  * @param prevState previous state
  //  * @param newState new state
  //  */
  // void onStateChange(int prevState, int newState) final;

  // variable for speed
  double current_speed_;

private:
  // module and class names
  torch::jit::script::Module module_;
  std::vector<std::string> class_names_;

  // variables for different thresholds
  float conf_threshold_;
  float iou_threshold_;

  // subscribers
  rclcpp::Subscription<std_msgs::msg::Float64>::SharedPtr speed_subscriber_;
};

#endif  // PSAF_SIGN_DETECTION__SIGN_DETECTION_NODE_HPP_

my Cmakelist is:

cmake_minimum_required(VERSION 3.5)
project(psaf_sign_detection)

set(NODE_NAME "sign_detection")

# Default to C99
if(NOT CMAKE_C_STANDARD)
  set(CMAKE_C_STANDARD 99)
endif()

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(libpsaf REQUIRED)
find_package(libpsaf_msgs REQUIRED)
find_package(OpenCV REQUIRED)

list(APPEND CMAKE_PREFIX_PATH "/home/tuodou/wise-2023-24-ctrl-alt-defeat-feature-lane-detection/src/install/psaf_configuration/share/psaf_configuration/cmake/")
list(APPEND CMAKE_PREFIX_PATH "/home/tuodou/kineto/libkineto/build")

set(psaf_configuration_DIR "/home/tuodou/wise-2023-24-ctrl-alt-defeat-feature-lane-detection/src/install/psaf_configuration/share/psaf_configuration/cmake/psaf_configurationConfig.cmake")

find_package(psaf_configuration REQUIRED)

# Explicitly set CUDA toolkit root directory
set(CUDA_TOOLKIT_ROOT_DIR /usr/local/cuda)

# Find CUDA
find_package(CUDA REQUIRED)

# find libtorch library

set(Torch_DIR /home/tuodou/.local/lib/python3.8/site-packages/torch/share/cmake/Torch)
find_package(Torch REQUIRED)

# define sign model directory
add_compile_definitions(SIGN_MODEL_DIR="${CMAKE_CURRENT_SOURCE_DIR}/models")

add_executable(${NODE_NAME}
    src/${NODE_NAME}.cpp
        src/${NODE_NAME}_node.cpp
        include/psaf_sign_detection/definitions.hpp)

target_include_directories(${NODE_NAME} PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
        ${OpenCV_INCLUDE_DIRS})

target_link_libraries(${NODE_NAME}
    ${OpenCV_LIBS}
        ${TORCH_LIBRARIES}
    ${CUDA_LIBRARIES} 
        zbar 
        )

ament_target_dependencies(${NODE_NAME} rclcpp std_msgs libpsaf OpenCV psaf_configuration Torch)

install(TARGETS
        ${NODE_NAME}
    DESTINATION lib/${PROJECT_NAME})

install(DIRECTORY
        launch
        config
        DESTINATION share/${PROJECT_NAME})

ament_package()
```
$\endgroup$
1
  • $\begingroup$ Welcome to Robotics Stack Exchange! Seeing the undefined reference to rclcpp::shutdown, I wonder if your ROS is appropriately installed in the first place. Please share your environment details. Also, please tell us how you installed the ROS. A link to the documentation you followed will be much more appreciated. $\endgroup$
    – ravi
    Commented Dec 5, 2023 at 9:35

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.