Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement teleop Keyboard #3

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
11 changes: 11 additions & 0 deletions serial_motor_demo/config/robot_params.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# robot_params.yaml
wheel_odometry_node:
ros__parameters:
wheel_radius: 0.058 # Meters
wheel_base: 0.284 # Meters
# Add other parameters as needed

motor_command_node:
ros__parameters:
wheel_radius: 0.058 # Meters
wheel_base: 0.284 # Meters
65 changes: 65 additions & 0 deletions serial_motor_demo/launch/motor_driver.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# my_launch_file.launch.py

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():

config = os.path.join(
get_package_share_directory('serial_motor_demo'),
'config',
'robot_params.yaml'
)

return LaunchDescription([
DeclareLaunchArgument(
'speed', default_value='0.3',
description='Speed for teleop_twist_keyboard'),
DeclareLaunchArgument(
'turn', default_value='0.5',
description='Turn for teleop_twist_keyboard'),
DeclareLaunchArgument(
'serial_port', default_value='/dev/ttyUSB0'),


Node(
package='serial_motor_demo',
executable='driver',
name='driver',
output='screen',
parameters=[
{'serial_port': LaunchConfiguration('serial_port')},
],
),
Node(
package='teleop_twist_keyboard',
executable='teleop_twist_keyboard',
name='teleop_twist_keyboard',
output='screen',
prefix='xterm -e',
parameters=[
{'speed': LaunchConfiguration('speed')},
{'turn': LaunchConfiguration('turn')},
],
),
Node(
package='serial_motor_demo',
executable='motor_command_node',
name='motor_command_node',
output='screen',
parameters=[config],

),

Node(
package='serial_motor_demo',
executable='wheels_odometry',
name='wheel_odometry_node',
output='screen',
parameters=[config],
),
])
Binary file added serial_motor_demo/motor_driver_launch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions serial_motor_demo/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>

<exec_depend>geometry_msgs</exec_depend>
<exec_depend>tf2_ros</exec_depend>

<depend>rclpy</depend>
<depend>serial_motor_demo_msgs</depend>
Expand Down
6 changes: 3 additions & 3 deletions serial_motor_demo/serial_motor_demo/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ def __init__(self):

# Setup parameters

self.declare_parameter('encoder_cpr', value=0)
self.declare_parameter('encoder_cpr', value=17200)
if (self.get_parameter('encoder_cpr').value == 0):
print("WARNING! ENCODER CPR SET TO 0!!")


self.declare_parameter('loop_rate', value=0)
self.declare_parameter('loop_rate', value=30)
if (self.get_parameter('loop_rate').value == 0):
print("WARNING! LOOP RATE SET TO 0!!")


self.declare_parameter('serial_port', value="/dev/ttyUSB0")
self.declare_parameter('serial_port', value="/dev/ttyACM0")
self.serial_port = self.get_parameter('serial_port').value


Expand Down
69 changes: 69 additions & 0 deletions serial_motor_demo/serial_motor_demo/motor_command_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from serial_motor_demo_msgs.msg import MotorCommand
import math

class MotorCommandNode(Node):
def __init__(self):
super().__init__('motor_command_node')

# Create a subscriber to listen to Twist messages
self.create_subscription(
Twist,
'cmd_vel', # Topic name might be different based on your setup
self.twist_callback,
10
)

# Create a publisher to send out MotorCommand messages
self.publisher = self.create_publisher(MotorCommand, 'motor_command', 10)

# Especificaciones del robot
self.declare_parameter('wheel_radius', 0.0553, ignore_override=True)
self.declare_parameter('wheel_base', 0.284, ignore_override=True)

# Retrieve parameter values
self.wheel_radius = self.get_parameter('wheel_radius').value
self.wheel_base = self.get_parameter('wheel_base').value


self.get_logger().info('Motor Command Node Started')

def twist_callback(self, msg):
try:
# Assuming linear.x controls the speed and angular.z controls the steering
speed = msg.linear.x
rotation = msg.angular.z



mot_1_speed = (speed + (rotation * self.wheel_base / 2)) / self.wheel_radius
mot_2_speed = (speed - (rotation * self.wheel_base / 2)) / self.wheel_radius

# Convert to rad/s
mot_1_speed = mot_1_speed / (2 * math.pi)
mot_2_speed = mot_2_speed / (2 * math.pi)

# Create and publish the motor command
motor_msg = MotorCommand()
motor_msg.is_pwm = False
motor_msg.mot_1_req_rad_sec = mot_1_speed
motor_msg.mot_2_req_rad_sec = mot_2_speed
self.publisher.publish(motor_msg)

self.get_logger().info(f'Motor Commands sent: mot_1 = {mot_1_speed:.2f} rad/s, mot_2 = {mot_2_speed:.2f} rad/s')

except Exception as e:
self.get_logger().error(f'Error in twist_callback: {str(e)}')



def main(args=None):
rclpy.init(args=args)
node = MotorCommandNode()
rclpy.spin(node)
rclpy.shutdown()

if __name__ == '__main__':
main()
87 changes: 87 additions & 0 deletions serial_motor_demo/serial_motor_demo/wheels_odometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist, Pose, Quaternion, Point, TransformStamped
from nav_msgs.msg import Odometry
from utils.transformations import euler2quaternion
from math import sin, cos
import tf2_ros

from serial_motor_demo_msgs.msg import MotorVels


class OdometryNode(Node):
def __init__(self):
super().__init__('wheel_odometry_node')
self.subscription = self.create_subscription(
MotorVels, 'motor_vels', self.listener_callback, 10)
self.publisher = self.create_publisher(Odometry, 'wheel_odom', 10)
self.odom_broadcaster = tf2_ros.TransformBroadcaster(self)
self.last_time = self.get_clock().now()

# Estado inicial del robot
self.x = 0.0
self.y = 0.0
self.theta = 0.0

# Especificaciones del robot
self.wheel_radius = self.declare_parameter('wheel_radius', 0.0553).value
self.wheel_base = self.declare_parameter('wheel_base', 0.284).value

def listener_callback(self, msg):
current_time = self.get_clock().now()
dt = (current_time - self.last_time).nanoseconds / 1e9
self.last_time = current_time

# Calcular velocidades lineales de las ruedas
v_left = msg.mot_1_rad_sec * self.wheel_radius
v_right = msg.mot_2_rad_sec * self.wheel_radius

# Calcular la velocidad lineal y angular del robot
V = (v_right + v_left) / 2
omega = (v_right - v_left) / self.wheel_base

# Actualizar la posición y orientación
self.x += V * cos(self.theta) * dt
self.y += V * sin(self.theta) * dt
self.theta += omega * dt

# Crear y llenar el mensaje de odometría
odom = Odometry()
odom.header.stamp = current_time.to_msg()
odom.header.frame_id = "odom"
odom.child_frame_id = "base_link"
orientation=euler2quaternion(0, 0, self.theta)
odom.pose.pose = Pose(
position=Point(x=self.x, y=self.y, z=0.0),
orientation=Quaternion(x=orientation[0], y=orientation[1], z=orientation[2], w=orientation[3])
)

# Aquí puedes agregar la velocidad (Twist) si es necesario

self.publisher.publish(odom)

# Enviar transformación
t = TransformStamped()
t.header.stamp = current_time.to_msg()
t.header.frame_id = 'odom'
t.child_frame_id = 'base_link'
t.transform.translation.x = self.x
t.transform.translation.y = self.y
quaternion = euler2quaternion(0, 0, self.theta)
# Use the quaternion for rotation
t.transform.rotation.x = quaternion[0]
t.transform.rotation.y = quaternion[1]
t.transform.rotation.z = quaternion[2]
t.transform.rotation.w = quaternion[3]

self.odom_broadcaster.sendTransform(t)

def main(args=None):
rclpy.init(args=args)
odometry_node = OdometryNode()
rclpy.spin(odometry_node)
odometry_node.destroy_node()
rclpy.shutdown()

if __name__ == '__main__':
main()
4 changes: 2 additions & 2 deletions serial_motor_demo/setup.cfg
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[develop]
script-dir=$base/lib/serial_motor_demo
script_dir=$base/lib/serial_motor_demo
[install]
install-scripts=$base/lib/serial_motor_demo
install_scripts=$base/lib/serial_motor_demo
21 changes: 15 additions & 6 deletions serial_motor_demo/setup.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
from setuptools import setup
import os
from glob import glob

package_name = 'serial_motor_demo'

setup(
name=package_name,
version='0.0.0',
packages=[package_name],
py_modules=['utils.transformations'],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
# Include launch files
(os.path.join('share', package_name, 'launch'), glob('launch/*.launch.py')),
(os.path.join('share', package_name, 'config'), glob('config/*.yaml')),
],
install_requires=['setuptools'],
zip_safe=True,
Expand All @@ -19,9 +25,12 @@
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'gui = serial_motor_demo.gui:main',
'driver = serial_motor_demo.driver:main'
],
},
'console_scripts': [
'gui = serial_motor_demo.gui:main',
'driver = serial_motor_demo.driver:main',
'motor_command_node = serial_motor_demo.motor_command_node:main',
'wheels_odometry = serial_motor_demo.wheels_odometry:main',
],
},

)
Empty file.
Loading