myAGV Plus + 270M5 手柄遥控案例
功能:用手柄控制myAGV Plus + 270M5进行移动
1 硬件安装
机械臂安装
将270M5固定在AGV上


然后将12V电源线,Type-C线,手柄接收器参考下图进行接线后,按下AGV电源键即可

开机后,确保270M5底部屏幕显示ATOM:OK

末端工具可选择夹爪或吸泵
吸泵安装
将乐高连接件插入吸泵上预留的插孔中

将插好连接件的吸泵对准机械臂末端插孔插入

然后将公母杜邦线接到机械臂的底座IO

左侧为吸泵引脚,右侧为机械臂引脚 GND -> GND 5V -> 5V G2 -> 2 G5 -> 5
夹爪安装
将乐高连接件插入夹爪预留的插孔中

将插好连接件的夹爪对准机械臂末端插孔插入

将夹爪线插入机械臂控制接口

2 依赖库安装
pip install pygame pymycobot --upgrade
3 手柄功能说明

4 手柄激活
将手柄的开关打开

执行程序后,长按手柄的MODE键,待手柄的MODE灯亮红灯后,即可松开MODE键


注意:只有MODE LED亮灯,才可以控制机械臂,如果手柄长时间不使用会进入待机状态,可以按一下手柄的START按键进行激活

5 案列复现
启动里程计节点和激光雷达节点
ros2 launch myagv_plus_bringup myagv_plus_bringup
案列程序
运行下面程序后,终端打印以下信息后即可开始控制: 机械臂控制器初始化成功
手柄连接成功
#!/usr/bin/env python3
"""
手柄控制节点
- 接收手柄输入
- 发布速度控制指令
"""
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from myagv_plus_joystick_control.joystick import InputJoystick, Hotkey
from myagv_plus_joystick_control.mecharm_controller import MechArm270Controller
from pymycobot import MechArm270
class JoystickController(Node):
def __init__(self):
super().__init__('joystick_controller')
# 速度参数
self.max_linear_speed = 0.25 # 最大线速度
self.max_angular_speed = 1.0 # 最大角速度
# 当前速度
self.linear_speed_x = 0.0 # 前后移动
self.linear_speed_y = 0.0 # 左右平移
self.angular_speed = 0.0 # 旋转
# 机械臂控制标志
self._arm_motion_control = False
try:
self.arm_controller = MechArm270Controller(port='/dev/ttyACM0') # 根据实际端口修改
self.arm_controller.set_fresh_mode(0) # 0: 自动模式, 1: 刷新模式
self.get_logger().info("机械臂控制器初始化成功")
except Exception as e:
self.get_logger().error(f"机械臂控制器初始化失败: {e}")
self.arm_controller = None
# 发布速度指令
self.cmd_vel_publisher = self.create_publisher(Twist, '/cmd_vel', 10)
# 初始化手柄
self.joystick = InputJoystick(raw_mapping=False)
self.joystick.inject_caller(self)
# 注册手柄事件
self.register_joystick_events()
# 检测手柄
if self.init_joystick():
self.get_logger().info("手柄连接成功")
# 启动手柄监听线程
import threading
self.joystick_thread = threading.Thread(target=self.joystick.run_with_loop)
self.joystick_thread.daemon = True
self.joystick_thread.start()
# 启动速度发送定时器(10Hz)
self.create_timer(0.1, self.send_speed_timer)
else:
self.get_logger().error("未检测到手柄")
def init_joystick(self) -> bool:
device = InputJoystick.get_gamepad(0)
if device is not None:
self.joystick.set_device(device)
return True
return False
def register_joystick_events(self):
"""注册手柄事件回调"""
# 右摇杆 - 左右移动(平移,线速度Y)
@self.joystick.register(hotkey=Hotkey.RIGHT_X_AXIS)
def RIGHT_X_AXIS(self, value: int):
# 0-127: 左移, 128: 中心, 129-255: 右移
if value < 128:
self.linear_speed_y = self.max_linear_speed # 线速度Y(左移)
elif value > 128:
self.linear_speed_y = -self.max_linear_speed # 线速度Y(右移)
else:
self.linear_speed_y = 0.0
self.publish_speed()
# 右摇杆 - 前后移动(线速度X)
@self.joystick.register(hotkey=Hotkey.RIGHT_Y_AXIS)
def RIGHT_Y_AXIS(self, value: int):
# 0-127: 前移, 128: 中心, 129-255: 后移
if value < 128:
self.linear_speed_x = self.max_linear_speed # 线速度X(前移)
elif value > 128:
self.linear_speed_x = -self.max_linear_speed # 线速度X(后移)
else:
self.linear_speed_x = 0.0
self.publish_speed()
# L2 - 速度减小(按钮事件,值为0或1)
@self.joystick.register(hotkey=Hotkey.L2, value_filter=lambda value: value == 1)
def L2(self, value: int):
self.max_linear_speed = max(0.1, self.max_linear_speed - 0.1)
self.max_angular_speed = max(0.5, self.max_angular_speed - 0.2)
self.get_logger().info(f"速度减小: 线速度={self.max_linear_speed:.2f}, 角速度={self.max_angular_speed:.2f}")
# R2 - 速度增大(按钮事件,值为0或1)
@self.joystick.register(hotkey=Hotkey.R2, value_filter=lambda value: value == 1)
def R2(self, value: int):
self.max_linear_speed = min(1.6, self.max_linear_speed + 0.1)
self.max_angular_speed = min(4.0, self.max_angular_speed + 0.2)
self.get_logger().info(f"速度增大: 线速度={self.max_linear_speed:.2f}, 角速度={self.max_angular_speed:.2f}")
# L1 - 车左旋
@self.joystick.register(hotkey=Hotkey.L1)
def L1(self, value: int):
if value == 1:
self.angular_speed = self.max_angular_speed # 角速度
else:
self.angular_speed = 0.0
self.publish_speed()
# R1 - 车右旋
@self.joystick.register(hotkey=Hotkey.R1)
def R1(self, value: int):
if value == 1:
self.angular_speed = -self.max_angular_speed # 角速度
else:
self.angular_speed = 0.0
self.publish_speed()
# START - 停止并回到零点
@self.joystick.register(hotkey=Hotkey.STARTUP, value_filter=lambda value: value == 0)
def STARTUP(self, value: int):
self.linear_speed_x = 0.0
self.linear_speed_y = 0.0
self.angular_speed = 0.0
self.publish_speed()
self.arm_controller.go_home()
self.get_logger().info("机械臂复位")
@self.joystick.register(hotkey=Hotkey.Y)
def Y(self, value: int):
print(f"{Hotkey.Y} {value}")
if value == 1:
# print(f" # Open gripper.")
self.arm_controller.open_gripper(1)
else:
self.arm_controller.stop()
# print(f" # Stop opening the gripper.")
@self.joystick.register(hotkey=Hotkey.A)
def A(self, value: int):
if value == 1:
# print(f" # Close gripper.")
self.arm_controller.close_gripper(1)
else:
self.arm_controller.stop()
# print(f" # Stop closing the gripper.")
@self.joystick.register(hotkey=Hotkey.X, value_filter=lambda value: value == 0)
def X(self, _: int):
try:
self.arm_controller.open_suction_pump()
# print(f"Open suction pump.")
except NotImplementedError as e:
print(e)
@self.joystick.register(hotkey=Hotkey.B, value_filter=lambda value: value == 0)
def B(self, _: int):
try:
self.arm_controller.close_suction_pump()
# print(f"Close suction pump.")
except NotImplementedError as e:
print(e)
@self.joystick.register(hotkey=Hotkey.HORIZONTAL)
def HORIZONTAL(self, value: int):
if value == 1:
# print(f" # The end is rotated counterclockwise.")
self.arm_controller.set_end_rotate(direction=-1, speed=20)
elif value == -1:
# print(f" # The end is rotate clockwise.")
self.arm_controller.set_end_rotate(direction=1, speed=20)
else:
# print(" # Stop spinning.")
self.arm_controller.stop()
@self.joystick.register(hotkey=Hotkey.VERTICAL)
def VERTICAL(self, value: int):
if value == -1:
# print(f"axis z +.")
self.arm_controller.coordinate(axis=3, direction=1)
elif value == 1:
# print(f"axis Z -.")
self.arm_controller.coordinate(axis=3, direction=0)
else:
# print("axis z stop.")
self.arm_controller.stop()
@self.joystick.register(hotkey=Hotkey.LEFT_X_AXIS)
def LEFT_X_AXIS(self, value: int):
if value > 128 and not self._arm_motion_control:
# print(f"axis y -.") # Y+
self.arm_controller.coordinate(axis=2, direction=0)
self._arm_motion_control = True
elif value < 128 and not self._arm_motion_control:
# print(f"axis y +.") # Y-
self.arm_controller.coordinate(axis=2, direction=1)
self._arm_motion_control = True
elif value == 128 and self._arm_motion_control:
self._arm_motion_control = False
# print(f"axis y stop.")
self.arm_controller.stop()
@self.joystick.register(hotkey=Hotkey.LEFT_Y_AXIS)
def LEFT_Y_AXIS(self, value: int):
if value < 128 and not self._arm_motion_control:
# print(f"axis x +.") # X+
self.arm_controller.coordinate(axis=1, direction=1)
self._arm_motion_control = True
elif value > 128 and not self._arm_motion_control:
# print(f"axis x -.") # X-
self.arm_controller.coordinate(axis=1, direction=0)
self._arm_motion_control = True
elif value == 128 and self._arm_motion_control:
self._arm_motion_control = False
# print(f"axis x stop.")
self.arm_controller.stop()
def publish_speed(self):
"""发布速度指令"""
twist = Twist()
twist.linear.x = self.linear_speed_x # 前后移动
twist.linear.y = self.linear_speed_y # 左右平移
twist.angular.z = self.angular_speed # 旋转
self.cmd_vel_publisher.publish(twist)
def send_speed_timer(self):
"""定时器回调,定期发送速度指令"""
self.publish_speed()
def main(args=None):
rclpy.init(args=args)
node = JoystickController()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
6 案例展示
