Executable not found on the libexec directory when trying to launch

Trying to do the basic wall following robot as part of a course and running into this error:

launch.substitutions.substitution_failure.SubstitutionFailure: executable 'follow_wall' not found on the libexec directory '/home/user/ros2_ws/install/wall_follower/lib/wall_follower'

when I try to run:

ros2 launch wall_follower start_wall_following.launch.py

My file structure looks like this:

The 3 files, shown below:

follow_wall

import rclpy
# import the ROS2 python libraries
from rclpy.node import Node
# import the Twist module from geometry_msgs interface
from geometry_msgs.msg import Twist
# import the LaserScan module from sensor_msgs interface
from sensor_msgs.msg import LaserScan
from rclpy.qos import ReliabilityPolicy, QoSProfile


class WallFollow(Node):

    def __init__(self):
        # Here you have the class constructor
        # call the class constructor
        super().__init__('follow_wall')
        # create the publisher object
        self.publisher_ = self.create_publisher(Twist, 'cmd_vel', 10)
        # create the subscriber object
        self.subscriber = self.create_subscription(
            LaserScan, '/scan', self.laser_callback, QoSProfile(depth=10, reliability=ReliabilityPolicy.BEST_EFFORT))
        # define the timer period for 0.5 seconds
        self.timer_period = 0.5
        # define the variable to save the received info
        self.laser_forward = 0
        self.laser_right = 0
        # create a Twist message
        self.cmd = Twist()
        self.timer = self.create_timer(self.timer_period, self.motion)

    def laser_callback(self, msg):
        # Save the laser info
        self.laser_forward = msg.ranges[359]
        self.laser_right = msg.ranges[90]

    def motion(self):
        # print the data
        self.get_logger().info('I receive: "%s"' % str(self.laser_forward))
        # Logic of move
        if self.laser_right > 0.3:
            self.cmd.linear.x = 0.5
            self.cmd.angular.z = -0.2
        elif self.laser_right < 0.2:
            self.cmd.linear.x = 0.5
            self.cmd.angular.z = 0.2
        else:
            self.cmd.linear.x = 0.5
            self.cmd.angular.z = 0.0

        if self.laser_forward < 0.5:
            self.cmd.angular.z = 0.5

        # Publishing the cmd_vel values to a Topic
        self.publisher_.publish(self.cmd)


def main(args=None):
    # initialize the ROS communication
    rclpy.init(args=args)
    # declare the node constructor
    follow_wall = WallFollow()
    # pause the program execution, waits for a request to kill the node (ctrl+c)
    rclpy.spin(follow_wall)
    # Explicity destroy the node
    follow_wall.destroy_node()
    # shutdown the ROS communication
    rclpy.shutdown()


if __name__ == '__main__':
    main()

setup.py

from setuptools import setup
import os
from glob import glob

package_name = 'wall_follower'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name), glob('launch/*.launch.py'))
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='somebody very awesome',
    maintainer_email='user@user.com',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'simple_subscriber = wall_follower.follow_wall:main'
        ],
    },
)

wall_follower start_wall_following.launch.py

from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
    return LaunchDescription([
        Node(
            package='wall_follower',
            executable='follow_wall',
            output='screen'),
    ])

EDIT
ok one mistake I found was that this line in setup.py:

  entry_points={
        'console_scripts': [
            'simple_subscriber = wall_follower.follow_wall:main'
        ],
    },

should not say simple subscriber but instead wall_follower. Fixing that did bring progress and now a new error has arisen (full output of attempted launch):

INFO] [launch]: All log files can be found below /home/user/.ros/log/2023-01-01-21-08-08-165490-2_xterm-22022
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [follow_wall-1]: process started with pid [22084]
[follow_wall-1] Traceback (most recent call last):
[follow_wall-1]   File "/home/user/ros2_ws/install/wall_follower/lib/wall_follower/follow_wall", line 33, in <module>
[follow_wall-1]     sys.exit(load_entry_point('wall-follower==0.0.0', 'console_scripts', 'follow_wall')())
[follow_wall-1]   File "/home/user/ros2_ws/install/wall_follower/lib/python3.8/site-packages/wall_follower/follow_wall.py", line 61, in main
[follow_wall-1]     follow_wall = WallFollow()
[follow_wall-1]   File "/home/user/ros2_ws/install/wall_follower/lib/python3.8/site-packages/wall_follower/follow_wall.py", line 18, in __init__
[follow_wall-1]     self.publisher_ = self.create_publisher(Twist, 'cmd_vel', 10)
[follow_wall-1]   File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/node.py", line 1140, in create_publisher
[follow_wall-1]     check_for_type_support(msg_type)
[follow_wall-1]   File "/opt/ros/foxy/lib/python3.8/site-packages/rclpy/type_support.py", line 20, in check_for_type_support
[follow_wall-1]     ts = msg_type.__class__._TYPE_SUPPORT
[follow_wall-1] AttributeError: type object 'type' has no attribute '_TYPE_SUPPORT' This might be a ROS 1 message type but it should be a ROS 2 message type. Make sure to source your ROS 2 workspace after your ROS 1 workspace.
[ERROR] [follow_wall-1]: process has died [pid 22084, exit code 1, cmd '/home/user/ros2_ws/install/wall_follower/lib/wall_follower/follow_wall --ros-args'].

I also often get this warning when trying to source:

OS_DISTRO was set to 'noetic' before. Please make sure that the environment does not mix paths from different distributions.

I noticed there are different types of source commands, not sure which to use. I also noticed there was ros 1 and ros 2 ways of launching, and different sourcing for each, Very lost here on which to use, or generally how to get my code to run.

Hi @asengupt ,

Welcome to this Community!

If you are working on ROS2 Basics Rosject, each terminal you open are already pre-sourced with ROS2. So if you make any mistakes in your setup.py file, after fixing that, you just rebuild the package and source your install/setup.bash script.

Since you have already fixed your simple_subscriber issue, you can just rebuild your package and source setup.bash and you are good to go!

This is one of the most posted error in this forum and the solution is very simple - source the workspace before building (in case of ROS2 it is already sourced to ROS2, as said earlier) and source your setup.bash after colcon builds.

You get this note if you source ROS2 after ROS1. This is the procedure - so you can ignore this warning / output.

Regards,
Girish

2 Likes

Good afternoon
I have exactly the same problem but after building my package and doing source install/setup.sh in the ros2_ws.
My setup file seems to be correct.

I would like to know why it doesn’t work.

The error

File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/substitutions/executable_in_package.py", line 84, in perform
    raise SubstitutionFailure(
launch.substitutions.substitution_failure.SubstitutionFailure: executable 'wall_following' not found on the libexec directory '/home/user/ros2_ws/install/wall_follower/lib/wall_follower'

My main code

import rclpy
from rclpy.node import Node
from sensor_msgs.msg import LaserScan
from geometry_msgs.msg import Twist


class WallFollow(Node):
    def __init__(self):
        # Constructer of the node
        super().__init__('wall_follower')
        # Constructer of the publisher
        self.publisher = self.create_publisher(Twist, 'cmd_vel', 10)
        # Constructer of the subscriber
        self.subscriber = self.create_subscription(
            LaserScan, 'scan', self.laser_callback, 10)

        # Define the variables
        # Define the timer period for 0.5 seconds
        self.timer_period = 0.5
        # Initialisation of the laser_90_degree value at 0
        self.laser_90_degree = 0
        # Initialisation of the variable that will take the Twist message
        self.move = Twist()
        # Generate the action, here moving the robot with publication in the topic
        self.timer = self.create_timer(self.timer_period, self.motion)

    def laser_callback(self, sensor_msgs):
        self.laser_90_degree = sensor_msgs.ranges[90]

    def motion(self):
        # Print the degug function at the info level the information related to the laser
        self.get_logger().info('I receive: "%s"' % str(self.laser_90_degree))

        # Acion = movement of the robot according to the laser reading
        if(self.laser_90_degree > 0.3):  # Move forward and turn toward the wall
            self.move.linear.x = 0.5
            self.move.angular.z = 0.2
            print(1)

        elif(self.laser_90_degree > 0.2 and self.laser_90_degree < 0.3):  # Move forward
            self.move.linear.x = 0.5
            self.move.angular.z = 0.0
            print(2)

        else:
            if(self.laser_90_degree < 0.2):  # Move forward and turn backward the wall
                self.move.linear.x = 0.5
                self.move.angular.z = -0.2
                print(3)

            else:
                self.get_logger().info('Error! The distance is "%s"' % str(self.laser_90_degree))
                self.move.linear.x = 0.0
                self.move.angular.z = 0.0


def main(args=None):
    # Initialize the ROS communication
    rclpy.init(args=args)
    # Declare the node constructor
    WallFollower = WallFollow()
    # Pause the program execution, waits for a request to kill the node (ctrl+c)
    rclpy.spin(WallFollower)
    # Explicity destroy the node
    WallFollower.destroy_node()
    # Shutdown the ROS communication
    rclpy.shutdown()


if __name__ == '__main__':
    main()

My launch code

from launch import LaunchDescription
from launch_ros.actions import Node


def generate_launch_description():
    return LaunchDescription([
        Node(
            package='wall_follower',
            executable='wall_following',
            output='screen'),
    ])

My setup code

from setuptools import setup
import os
from glob import glob

package_name = 'wall_follower'

setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name), glob('launch/*.launch.py'))
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='user',
    maintainer_email='user@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'wall_followers = wall_follower.wall_following:main'
        ],
    },
)

Hi @ClairePottier ,

Welcome to this Community!

This line in your launch file:

must be the same as the left hand side variable in your setup.py file console_scripts

So here is your fix:
Replace this line: 'wall_followers = wall_follower.wall_following:main'
with this line: 'wall_following = wall_follower.wall_following:main'

Do not make any changes to your launch file.

Let me know if you still have problems.

Regards,
Girish

Hi Girish

It worked! Thanks!

Regards,
Claire

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.