0
$\begingroup$

New to ros2 here. I am using ros2 humble. I am taking to use ros2_control for the keyestudio ks0223 smart car (it uses gpio pins). I keep getting Waiting for /controller_manager to be available when I run my launch file. I also notice that I get duplicate robot_state_publisher nodes.

Command: ros2 launch my_robot my_robot.launch.py Output:

[INFO] [launch]: All log files can be found below /home/pi/.ros/log/2024-06-23-13-26-08-959242-raspberrypi-9127
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [robot_state_publisher-1]: process started with pid [9138]
[robot_state_publisher-1] [INFO] [1719145570.016500002] [robot_state_publisher]: got segment base_footprint
[robot_state_publisher-1] [INFO] [1719145570.016821944] [robot_state_publisher]: got segment base_link
[robot_state_publisher-1] [INFO] [1719145570.016872185] [robot_state_publisher]: got segment left_back_wheel_link
[robot_state_publisher-1] [INFO] [1719145570.016903000] [robot_state_publisher]: got segment left_front_wheel_link
[robot_state_publisher-1] [INFO] [1719145570.016929462] [robot_state_publisher]: got segment right_back_wheel_link
[robot_state_publisher-1] [INFO] [1719145570.016954129] [robot_state_publisher]: got segment right_front_wheel_link
[INFO] [ros2_control_node-2]: process started with pid [9152]
[INFO] [spawner-3]: process started with pid [9154]
[INFO] [spawner-4]: process started with pid [9156]
[ros2_control_node-2] [INFO] [1719145571.088513307] [controller_manager]: Subscribing to '~/robot_description' topic for robot description file.
[ros2_control_node-2] [INFO] [1719145571.094744619] [controller_manager]: update rate is 10 Hz
[ros2_control_node-2] [INFO] [1719145571.099397238] [controller_manager]: RT kernel is recommended for better performance
[spawner-3] [INFO] [1719145576.181104083] [spawner_joint_broad]: Waiting for '/controller_manager' services to be available
[spawner-4] [INFO] [1719145576.218799121] [spawner_diff_cont]: Waiting for '/controller_manager' services to be available
[spawner-3] [INFO] [1719145578.244257259] [spawner_joint_broad]: Waiting for '/controller_manager' services to be available
[spawner-4] [INFO] [1719145578.279637757] [spawner_diff_cont]: Waiting for '/controller_manager' services to be available
[spawner-3] [INFO] [1719145580.303168443] [spawner_joint_broad]: Waiting for '/controller_manager' services to be available
[spawner-4] [INFO] [1719145580.333648138] [spawner_diff_cont]: Waiting for '/controller_manager' services to be available
[spawner-3] [INFO] [1719145582.366774022] [spawner_joint_broad]: Waiting for '/controller_manager' services to be available
[spawner-4] [INFO] [1719145582.381937462] [spawner_diff_cont]: Waiting for '/controller_manager' services to be available
[spawner-3] [ERROR] [1719145584.007675581] [spawner_joint_broad]: Controller manager not available
[spawner-4] [ERROR] [1719145584.015068275] [spawner_diff_cont]: Controller manager not available
[ERROR] [spawner-3]: process has died [pid 9154, exit code 1, cmd '/opt/ros/humble/lib/controller_manager/spawner joint_broad --controller-manager /controller_manager --ros-args'].
[ERROR] [spawner-4]: process has died [pid 9156, exit code 1, cmd '/opt/ros/humble/lib/controller_manager/spawner diff_cont --controller-manager /controller_manager --ros-args'].

my_robot.launch.py:

def generate_launch_description():
    # Include rsp.launch.py
    rsp = IncludeLaunchDescription(
        PythonLaunchDescriptionSource([os.path.join(
            get_package_share_directory('my_robot'), 'bringup', 'launch', 'rsp.launch.py'
        )]), launch_arguments={'use_sim_time': 'false', 'use_ros2_control': 'true'}.items()
    )

    robot_controllers = os.path.join(get_package_share_directory('my_robot'), 'bringup', 'config', 'my_robot_controllers.yaml')

    # Control node
    control_node = Node(
        package='controller_manager',
        executable='ros2_control_node',
        parameters=[robot_controllers],
        output='both'
    )

    # TimerAction to delay the controller manager
    delayed_controller_manager = TimerAction(period=1.0, actions=[control_node])

    # Spawner nodes
    diff_drive_spawner = Node(
        package='controller_manager',
        executable='spawner',
        arguments=['diff_cont', '--controller-manager', '/controller_manager'],
    )

    joint_broad_spawner = Node(
        package='controller_manager',
        executable='spawner',
        arguments=['joint_broad', '--controller-manager', '/controller_manager'],
    )

    # Delayed spawner handlers
    delayed_diff_drive_spawner = RegisterEventHandler(
        event_handler=OnProcessStart(
            target_action=control_node,
            on_start=[diff_drive_spawner],
        )
    )

    delayed_joint_broad_spawner = RegisterEventHandler(
        event_handler=OnProcessStart(
            target_action=control_node,
            on_start=[joint_broad_spawner],
        )
    )

    return LaunchDescription([
        rsp,
        delayed_controller_manager,
        delayed_diff_drive_spawner,
        delayed_joint_broad_spawner
    ])

rsp.launch.py:

def generate_launch_description():

    # Check if we're told to use sim time
    use_sim_time = LaunchConfiguration('use_sim_time')
    use_ros2_control = LaunchConfiguration('use_ros2_control')

    # Process the URDF file
    pkg_path = os.path.join(get_package_share_directory('my_robot'))
    xacro_file = os.path.join(pkg_path,'description','urdf','my_robot.urdf.xacro')
    # robot_description_config = xacro.process_file(xacro_file).toxml()
    robot_description_config = Command(['xacro ', xacro_file, ' use_ros2_control:=', use_ros2_control, ' sim_mode:=', use_sim_time])
    
    # Create a robot_state_publisher node
    params = {'robot_description': robot_description_config, 'use_sim_time': use_sim_time}
    node_robot_state_publisher = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        name='robot_state_publisher',
        output='screen',
        parameters=[params]
    )


    # Launch!
    return LaunchDescription([
        DeclareLaunchArgument(
            'use_sim_time',
            default_value='false',
            description='Use sim time if true'),
        DeclareLaunchArgument(
            'use_ros2_control',
            default_value='true',
            description='Use ros2_control if true'),

        node_robot_state_publisher
    ])

my_robot_controllers.yaml:

controller_manager:
  ros__parameters:
    update_rate: 10


    diff_cont:
      type: diff_drive_controller/DiffDriveController

    joint_broad:
      type: joint_state_broadcaster/JointStateBroadcaster



diff_cont:
  ros__parameters:

    publish_rate: 10.0

    base_frame_id: base_footprint
    wheels_per_side: 2

    left_wheel_names: ["base_left_front_wheel_joint", "base_left_back_wheel_joint"]
    right_wheel_names: ["base_right_front_wheel_joint", "base_right_back_wheel_joint"]
    odom_frame_id: odom

    wheel_separation: 0.145
    wheel_radius: 0.03


    open_loop: true
    enable_odom_tf: true

    # cmd_vel_timeout: 0.5
    #publish_limited_velocity: true
    #velocity_rolling_window_size: 10

    # Velocity and acceleration limits
    # Whenever a min_* is unspecified, default to -max_*
    linear.x.has_velocity_limits: true
    linear.x.has_acceleration_limits: true
    linear.x.has_jerk_limits: false
    linear.x.max_velocity: 1.0
    linear.x.min_velocity: -1.0
    linear.x.max_acceleration: 1.0
    linear.x.max_jerk: 0.0
    linear.x.min_jerk: 0.0

    angular.z.has_velocity_limits: true
    angular.z.has_acceleration_limits: true
    angular.z.has_jerk_limits: false
    angular.z.max_velocity: 1.0
    angular.z.min_velocity: -1.0
    angular.z.max_acceleration: 1.0
    angular.z.min_acceleration: -1.0
    angular.z.max_jerk: 0.0
    angular.z.min_jerk: 0.0

    use_stamped_vel: false

// because I also believe I might have messed up my hardware interface: my_robot_hardware.hpp:

#ifndef MY_ROBOT_HARDWARE_HPP
#define MY_ROBOT_HARDWARE_HPP

#include <hardware_interface/system_interface.hpp>
#include <hardware_interface/handle.hpp>
#include <hardware_interface/types/hardware_interface_return_values.hpp>
#include <rclcpp/rclcpp.hpp>
#include <pigpiod_if2.h> // pigpio library header
#include "pluginlib/class_list_macros.hpp"

namespace my_robot
{
  class MyRobotHardware : public hardware_interface::SystemInterface
  {
  public:
    RCLCPP_SHARED_PTR_DEFINITIONS(MyRobotHardware);

    hardware_interface::CallbackReturn on_init(const hardware_interface::HardwareInfo &info) override;
    hardware_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State &previous_state) override;
    hardware_interface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &previous_state) override;

    hardware_interface::return_type read(const rclcpp::Time &time, const rclcpp::Duration &period) override;
    hardware_interface::return_type write(const rclcpp::Time &time, const rclcpp::Duration &period) override;

    std::vector<hardware_interface::StateInterface> export_state_interfaces() override;
    std::vector<hardware_interface::CommandInterface> export_command_interfaces() override;

  private:
    // Member variables to hold the state and command values
    std::vector<double> hw_states_;
    std::vector<double> hw_commands_;
    std::vector<std::string> joint_names_;
    size_t number_of_joints_;

    // Motor control pins
    int pi_;
    int L_IN1, L_IN2, L_PWM1, L_IN3, L_IN4, L_PWM2;
    int R_IN1, R_IN2, R_PWM1, R_IN3, R_IN4, R_PWM2;
  };
} // namespace my_robot

#endif // MY_ROBOT_HARDWARE_HPP_

my_robot_hardware.cpp:

#include "my_robot/my_robot_hardware.hpp"

namespace my_robot
{
    // Define the hardware interface constants if they are not defined
    const std::string HW_IF_POSITION = "position";
    const std::string HW_IF_VELOCITY = "velocity";

    hardware_interface::CallbackReturn MyRobotHardware::on_init(const hardware_interface::HardwareInfo &info)
    {
        if (hardware_interface::SystemInterface::on_init(info) != hardware_interface::CallbackReturn::SUCCESS)
        {
            return hardware_interface::CallbackReturn::ERROR;
        }

        number_of_joints_ = info.joints.size();
        hw_states_.resize(number_of_joints_, 0.0);
        hw_commands_.resize(number_of_joints_, 0.0);
        joint_names_.resize(number_of_joints_);

        for (size_t i = 0; i < number_of_joints_; ++i)
        {
            joint_names_[i] = info.joints[i].name;
        }

        // Initialize pigpio
        pi_ = pigpio_start(nullptr, nullptr);
        if (pi_ < 0)
        {
            RCLCPP_ERROR(rclcpp::get_logger("MyRobotHardware"), "Failed to initialize pigpio");
            return hardware_interface::CallbackReturn::ERROR;
        }

        // Define motor control pins
        L_IN1 = 20;
        L_IN2 = 21;
        L_PWM1 = 0;
        L_IN3 = 22;
        L_IN4 = 23;
        L_PWM2 = 1;
        R_IN1 = 24;
        R_IN2 = 25;
        R_PWM1 = 12;
        R_IN3 = 26;
        R_IN4 = 27;
        R_PWM2 = 13;

        // Set up motor control pins
        set_mode(pi_, L_IN1, PI_OUTPUT);
        set_mode(pi_, L_IN2, PI_OUTPUT);
        set_mode(pi_, L_PWM1, PI_OUTPUT);
        set_mode(pi_, L_IN3, PI_OUTPUT);
        set_mode(pi_, L_IN4, PI_OUTPUT);
        set_mode(pi_, L_PWM2, PI_OUTPUT);
        set_mode(pi_, R_IN1, PI_OUTPUT);
        set_mode(pi_, R_IN2, PI_OUTPUT);
        set_mode(pi_, R_PWM1, PI_OUTPUT);
        set_mode(pi_, R_IN3, PI_OUTPUT);
        set_mode(pi_, R_IN4, PI_OUTPUT);
        set_mode(pi_, R_PWM2, PI_OUTPUT);

        RCLCPP_INFO(rclcpp::get_logger("MyRobotHardware"), "Hardware interface initialized successfully.");
        return hardware_interface::CallbackReturn::SUCCESS;
    }

    hardware_interface::CallbackReturn MyRobotHardware::on_activate(const rclcpp_lifecycle::State & /*previous_state*/)
    {
        std::fill(hw_commands_.begin(), hw_commands_.end(), 0.0);
        RCLCPP_INFO(rclcpp::get_logger("MyRobotHardware"), "Hardware interface activated successfully.");
        return hardware_interface::CallbackReturn::SUCCESS;
    }

    hardware_interface::CallbackReturn MyRobotHardware::on_deactivate(const rclcpp_lifecycle::State & /*previous_state*/)
    {
        RCLCPP_INFO(rclcpp::get_logger("MyRobotHardware"), "Hardware interface deactivated successfully.");
        return hardware_interface::CallbackReturn::SUCCESS;
    }

    hardware_interface::return_type MyRobotHardware::read(const rclcpp::Time & /*time*/, const rclcpp::Duration & /*period*/)
    {
        // Read hardware state here (e.g., encoders)
        // For now, we are just simulating with dummy values
        hw_states_[0] = 1.0; // Left front wheel position
        hw_states_[1] = 1.0; // Left rear wheel position
        hw_states_[2] = 1.0; // Right front wheel position
        hw_states_[3] = 1.0; // Right rear wheel position

        return hardware_interface::return_type::OK;
    }

    hardware_interface::return_type MyRobotHardware::write(const rclcpp::Time & /*time*/, const rclcpp::Duration & /*period*/)
    {
        // Write commands to hardware here using pigpio
        int left_front_speed = static_cast<int>(hw_commands_[0] * 255);  // Scale command to PWM range
        int left_rear_speed = static_cast<int>(hw_commands_[1] * 255);   // Scale command to PWM range
        int right_front_speed = static_cast<int>(hw_commands_[2] * 255); // Scale command to PWM range
        int right_rear_speed = static_cast<int>(hw_commands_[3] * 255);  // Scale command to PWM range

        // Left front motor control
        if (left_front_speed >= 0)
        {
            gpio_write(pi_, L_IN1, 0);
            gpio_write(pi_, L_IN2, 1);
        }
        else
        {
            gpio_write(pi_, L_IN1, 1);
            gpio_write(pi_, L_IN2, 0);
        }
        set_PWM_dutycycle(pi_, L_PWM1, abs(left_front_speed));

        // Left rear motor control
        if (left_rear_speed >= 0)
        {
            gpio_write(pi_, L_IN3, 0);
            gpio_write(pi_, L_IN4, 1);
        }
        else
        {
            gpio_write(pi_, L_IN3, 1);
            gpio_write(pi_, L_IN4, 0);
        }
        set_PWM_dutycycle(pi_, L_PWM2, abs(left_rear_speed));

        // Right front motor control
        if (right_front_speed >= 0)
        {
            gpio_write(pi_, R_IN1, 1);
            gpio_write(pi_, R_IN2, 0);
        }
        else
        {
            gpio_write(pi_, R_IN1, 0);
            gpio_write(pi_, R_IN2, 1);
        }
        set_PWM_dutycycle(pi_, R_PWM1, abs(right_front_speed));

        // Right rear motor control
        if (right_rear_speed >= 0)
        {
            gpio_write(pi_, R_IN3, 1);
            gpio_write(pi_, R_IN4, 0);
        }
        else
        {
            gpio_write(pi_, R_IN3, 0);
            gpio_write(pi_, R_IN4, 1);
        }
        set_PWM_dutycycle(pi_, R_PWM2, abs(right_rear_speed));

        return hardware_interface::return_type::OK;
    }

    std::vector<hardware_interface::StateInterface> MyRobotHardware::export_state_interfaces()
    {
        std::vector<hardware_interface::StateInterface> state_interfaces;

        state_interfaces.emplace_back(hardware_interface::StateInterface(
            joint_names_[0], HW_IF_POSITION, &hw_states_[0]));
        state_interfaces.emplace_back(hardware_interface::StateInterface(
            joint_names_[1], HW_IF_POSITION, &hw_states_[1]));
        state_interfaces.emplace_back(hardware_interface::StateInterface(
            joint_names_[2], HW_IF_POSITION, &hw_states_[2]));
        state_interfaces.emplace_back(hardware_interface::StateInterface(
            joint_names_[3], HW_IF_POSITION, &hw_states_[3]));

        return state_interfaces;
    }

    std::vector<hardware_interface::CommandInterface> MyRobotHardware::export_command_interfaces()
    {
        std::vector<hardware_interface::CommandInterface> command_interfaces;

        command_interfaces.emplace_back(hardware_interface::CommandInterface(
            joint_names_[0], HW_IF_VELOCITY, &hw_commands_[0]));
        command_interfaces.emplace_back(hardware_interface::CommandInterface(
            joint_names_[1], HW_IF_VELOCITY, &hw_commands_[1]));
        command_interfaces.emplace_back(hardware_interface::CommandInterface(
            joint_names_[2], HW_IF_VELOCITY, &hw_commands_[2]));
        command_interfaces.emplace_back(hardware_interface::CommandInterface(
            joint_names_[3], HW_IF_VELOCITY, &hw_commands_[3]));

        return command_interfaces;
    }

} // namespace my_robot

// Register the hardware interface as a plugin
#include "pluginlib/class_list_macros.hpp"

PLUGINLIB_EXPORT_CLASS(my_robot::MyRobotHardware, hardware_interface::SystemInterface)

Things I tried:

  1. I tried changing the name of the node in rsp.launch.py to /robot_state_publisher_2 but it did not work.
  2. I went back to modify my yaml config file.

Sorry, I am used to the formatting here yet. I know this is quite long, but I am so confused.

$\endgroup$

1 Answer 1

0
$\begingroup$

First, I suggest to use the mock hardware component to avoid any problems with your custom component for now.

It seems that the controller_manager does not receive the robot_description topic. Use ros2 cli to see what happens with your nodes, e.g.

  • ros2 node list
  • ros2 topic info /robot_description
  • ros2 node info robot_state_publisher
  • ros2 node info controller_manager
$\endgroup$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.