feat: 初始化项目,添加电机控制、CAN通信、QT界面等模块
This commit is contained in:
BIN
motor_control/__pycache__/can_main.cpython-38.pyc
Normal file
BIN
motor_control/__pycache__/can_main.cpython-38.pyc
Normal file
Binary file not shown.
93
motor_control/can_main.py
Normal file
93
motor_control/can_main.py
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2025/7/21
|
||||
# @Author : hx
|
||||
# @File : can_main.py
|
||||
'''
|
||||
|
||||
import can
|
||||
import time
|
||||
|
||||
# 导入你写的 generate_position_command 函数
|
||||
from motor_control_can_all import generate_position_command
|
||||
|
||||
|
||||
# ========================================
|
||||
# 发送CAN帧函数
|
||||
# ========================================
|
||||
def send_can_frame(data, can_id=0x01, channel='vcan0', interface='socketcan'):
|
||||
"""
|
||||
发送CAN帧
|
||||
:param data: 7字节的列表
|
||||
:param can_id: CAN ID
|
||||
:param channel: CAN 通道
|
||||
:param interface: 总线类型
|
||||
"""
|
||||
try:
|
||||
bus = can.interface.Bus(channel=channel, interface=interface)
|
||||
msg = can.Message(arbitration_id=can_id, data=data, is_extended_id=False)
|
||||
bus.send(msg)
|
||||
print(f"[发送CAN帧] ID={can_id:02X} 数据=[{', '.join(f'0x{x:02X}' for x in data)}]")
|
||||
bus.shutdown()
|
||||
except Exception as e:
|
||||
print(f"[发送失败] {e}")
|
||||
|
||||
|
||||
# ========================================
|
||||
# 监听CAN反馈函数
|
||||
# ========================================
|
||||
def listen_can_feedback(channel='vcan0', interface='socketcan', timeout=2.0):
|
||||
"""
|
||||
监听是否有CAN反馈数据
|
||||
:param channel: CAN 通道
|
||||
:param interface: 总线类型
|
||||
:param timeout: 等待反馈的超时时间(秒)
|
||||
"""
|
||||
try:
|
||||
bus = can.interface.Bus(channel=channel, interface=interface)
|
||||
print(f"[监听反馈] 等待来自 {channel} 的反馈({timeout}秒超时)...")
|
||||
msg = bus.recv(timeout=timeout)
|
||||
if msg:
|
||||
print(f"[收到反馈] ID={msg.arbitration_id:02X} 数据=[{', '.join(f'0x{x:02X}' for x in msg.data)}]")
|
||||
else:
|
||||
print("[未收到反馈]")
|
||||
bus.shutdown()
|
||||
except Exception as e:
|
||||
print(f"[监听失败] {e}")
|
||||
|
||||
|
||||
# ========================================
|
||||
# 主函数:发送位置控制指令并监听反馈
|
||||
# ========================================
|
||||
|
||||
def can_motor_contral(direction=0, angle=100, microstep=32, can_id=0x01, channel='vcan0', listen_feedback=False):
|
||||
print("=== 开始发送CAN位置控制指令 ===")
|
||||
print(f"参数:方向={direction}(0=逆时针,1=顺时针),角度={angle}°,细分={microstep}")
|
||||
|
||||
# 生成CAN数据帧
|
||||
can_data = generate_position_command(direction=direction, microstep=microstep, angle=angle)
|
||||
print(f"生成的CAN数据帧: [{', '.join(f'0x{x:02X}' for x in can_data)}]")
|
||||
|
||||
# 发送CAN帧
|
||||
send_can_frame(can_data, can_id=can_id, channel=channel)
|
||||
|
||||
# 如果启用监听,等待反馈
|
||||
if listen_feedback:
|
||||
listen_can_feedback(channel=channel)
|
||||
|
||||
print("=== 电机控制流程完成 ===")
|
||||
|
||||
# ========================================
|
||||
# 程序入口(直接运行时使用)
|
||||
# ========================================
|
||||
if __name__ == "__main__":
|
||||
# 这里写死参数,方便调试
|
||||
can_motor_contral(
|
||||
direction=1, # 顺时针
|
||||
angle=180, # 180度
|
||||
microstep=16, # 细分值16
|
||||
can_id=0x02, # CAN ID 0x02
|
||||
channel='vcan0', # 使用虚拟CAN
|
||||
listen_feedback=True # 是否监听反馈
|
||||
)
|
||||
13
motor_control/can_run_demo.py
Normal file
13
motor_control/can_run_demo.py
Normal file
@ -0,0 +1,13 @@
|
||||
# can_run_demo.py
|
||||
|
||||
from can_main import can_motor_contral
|
||||
|
||||
if __name__ == "__main__":
|
||||
can_motor_contral(
|
||||
direction=1,
|
||||
angle=90,
|
||||
microstep=32,
|
||||
can_id=0x03,
|
||||
channel='vcan0',
|
||||
listen_feedback=True
|
||||
)
|
||||
105
motor_control/can_test.py
Normal file
105
motor_control/can_test.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2025/7/15 17:52
|
||||
# @Author : hx
|
||||
# @File : motor.py
|
||||
'''
|
||||
|
||||
import time
|
||||
|
||||
start_speed = 8
|
||||
stop_speed = 0
|
||||
|
||||
# 细分值映射表
|
||||
MICROSTEP_MAP = {
|
||||
2: 0x01,
|
||||
4: 0x02,
|
||||
8: 0x04,
|
||||
16: 0x10,
|
||||
32: 0x20
|
||||
}
|
||||
|
||||
def generate_speed_command(direction=1, microstep=32, speed=10):
|
||||
"""
|
||||
生成速度控制模式的 CAN 数据帧(7字节)
|
||||
:param direction: 0=逆时针,1=顺时针
|
||||
:param microstep: 细分值(2, 4, 8, 16, 32)
|
||||
:param speed: 速度值(Rad/s)
|
||||
:return: 7字节 CAN 数据帧
|
||||
"""
|
||||
control_mode = 0x01 # 速度控制模式
|
||||
direction_byte = 0x01 if direction == 1 else 0x00
|
||||
microstep_byte = MICROSTEP_MAP.get(microstep, 0x20)
|
||||
|
||||
raw_speed = int(speed * 10)
|
||||
speed_high = (raw_speed >> 8) & 0xFF
|
||||
speed_low = raw_speed & 0xFF
|
||||
|
||||
return [control_mode, direction_byte, microstep_byte, 0x00, 0x00, speed_high, speed_low]
|
||||
|
||||
|
||||
def generate_position_command(angle, speed=10, direction=1, microstep=32):
|
||||
"""
|
||||
生成位置控制模式的 CAN 数据帧(7字节)
|
||||
:param angle: 目标角度(度)
|
||||
:param speed: 速度值(Rad/s)
|
||||
:param direction: 0=逆时针,1=顺时针
|
||||
:param microstep: 细分值(2, 4, 8, 16, 32)
|
||||
:return: 7字节 CAN 数据帧
|
||||
"""
|
||||
control_mode = 0x02 # 位置控制模式
|
||||
direction_byte = 0x01 if direction == 1 else 0x00
|
||||
microstep_byte = MICROSTEP_MAP.get(microstep, 0x20)
|
||||
|
||||
raw_angle = int(angle * 10)
|
||||
pos_high = (raw_angle >> 8) & 0xFF
|
||||
pos_low = raw_angle & 0xFF
|
||||
|
||||
raw_speed = int(speed * 10)
|
||||
speed_high = (raw_speed >> 8) & 0xFF
|
||||
speed_low = raw_speed & 0xFF
|
||||
|
||||
return [control_mode, direction_byte, microstep_byte, pos_high, pos_low, speed_high, speed_low]
|
||||
|
||||
|
||||
def send_can_frame(data, can_id=0x01):
|
||||
"""
|
||||
模拟发送CAN帧(只打印,不实际发送)
|
||||
"""
|
||||
print(f"[发送CAN帧] ID={can_id:02X} 数据=[{', '.join(f'0x{x:02X}' for x in data)}]")
|
||||
|
||||
|
||||
def listen_and_respond():
|
||||
"""
|
||||
模拟监听并响应信号的逻辑
|
||||
"""
|
||||
try:
|
||||
print("[监听中] 等待信号...")
|
||||
|
||||
# 模拟收到信号后执行动作
|
||||
time.sleep(2)
|
||||
|
||||
# 示例:位置控制模式 - 顺时针转动 1000 度,速度 10Rad/s,32细分
|
||||
pos_data = generate_position_command(angle=1000, speed=10, direction=1, microstep=32)
|
||||
send_can_frame(pos_data, can_id=0x01)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
# 示例:速度控制模式 - 顺时针,速度 8Rad/s,32细分
|
||||
speed_data = generate_speed_command(direction=1, microstep=32, speed=8)
|
||||
send_can_frame(speed_data, can_id=0x01)
|
||||
|
||||
print("[已完成一次响应]")
|
||||
|
||||
except Exception as e:
|
||||
print(f"监听时发生错误: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
# 示例:执行一次电机控制流程
|
||||
listen_and_respond()
|
||||
|
||||
except Exception as e:
|
||||
print(f"运行错误: {e}")
|
||||
134
motor_control/contral.py
Normal file
134
motor_control/contral.py
Normal file
@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2025/7/15 17:52
|
||||
# @Author : hx
|
||||
# @File : motor.py
|
||||
'''
|
||||
import serial
|
||||
import time
|
||||
import math
|
||||
|
||||
start_speed = 8
|
||||
stop_speed = 0
|
||||
|
||||
def send_hex_command(port, baudrate, hex_data):
|
||||
"""
|
||||
通过串口发送十六进制指令
|
||||
:param port: 串口号 (如 'COM3' 或 '/dev/ttyUSB0')
|
||||
:param baudrate: 波特率 (如 9600)
|
||||
:param hex_data: 十六进制字符串 (如 "7B 01 02 01 20 0E 10 00 64 23 7D")
|
||||
"""
|
||||
try:
|
||||
# 转换十六进制字符串为字节数据
|
||||
byte_data = bytes.fromhex(hex_data.replace(" ", ""))
|
||||
|
||||
# 打开串口
|
||||
with serial.Serial(port, baudrate, timeout=1) as ser:
|
||||
print(f"已连接串口 {port}, 波特率 {baudrate}")
|
||||
|
||||
# 发送指令
|
||||
ser.write(byte_data)
|
||||
print(f"已发送指令: {hex_data}")
|
||||
|
||||
# 可选:等待并读取返回数据
|
||||
time.sleep(0.1)
|
||||
if ser.in_waiting > 0:
|
||||
response = ser.read(ser.in_waiting)
|
||||
print(f"收到响应: {response.hex().upper()}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"发生错误: {e}")
|
||||
|
||||
def generate_fixed_command(speed):
|
||||
"""
|
||||
生成固定的速度控制指令: 7B 01 01 00 20 00 00 00 <speed> <checksum> 7D
|
||||
参数:
|
||||
speed (int): 十进制速度值 (0~255)
|
||||
返回:
|
||||
str: 十六进制命令字符串
|
||||
"""
|
||||
|
||||
actual_speed = int(speed * 10)
|
||||
|
||||
if not (0 <= actual_speed <= 255):
|
||||
raise ValueError("速度必须在 0~255 范围内")
|
||||
|
||||
command_bytes = [
|
||||
0x7B, 0x01, 0x01, # 帧头、地址、控制模式
|
||||
0x00, 0x20, # 方向、细分
|
||||
0x00, 0x00, # 位置高/低字节(持续旋转)
|
||||
0x00, actual_speed # 保持整数速度值
|
||||
]
|
||||
|
||||
# 计算校验和(前9个字节异或)
|
||||
checksum = 0
|
||||
for byte in command_bytes:
|
||||
checksum ^= byte
|
||||
|
||||
full_command = command_bytes + [checksum, 0x7D]
|
||||
return ' '.join(f'{byte:02X}' for byte in full_command)
|
||||
|
||||
def listen_and_respond(port, baudrate):
|
||||
"""
|
||||
持续监听串口,一旦有信号就执行开始和停止的指令
|
||||
"""
|
||||
try:
|
||||
with serial.Serial(port, baudrate, timeout=1) as ser:
|
||||
ser.reset_input_buffer()
|
||||
print(f"[监听中] 串口 {port} 已打开,等待信号...")
|
||||
|
||||
while True:
|
||||
if ser.in_waiting > 0:
|
||||
incoming = ser.read(ser.in_waiting)
|
||||
print(f"[收到信号] 内容: {incoming.hex().upper()}")
|
||||
|
||||
# 执行开始旋转
|
||||
start_cmd = generate_fixed_command(8)
|
||||
print("发送开始指令:", start_cmd)
|
||||
ser.write(bytes.fromhex(start_cmd.replace(" ", "")))
|
||||
|
||||
# 延迟一段时间
|
||||
time.sleep(4)
|
||||
|
||||
# 执行停止
|
||||
stop_cmd = generate_fixed_command(0)
|
||||
print("发送停止指令:", stop_cmd)
|
||||
ser.write(bytes.fromhex(stop_cmd.replace(" ", "")))
|
||||
|
||||
print("[已完成一次响应]\n等待下一次信号...\n")
|
||||
|
||||
time.sleep(0.1) # 避免CPU占用过高
|
||||
|
||||
except Exception as e:
|
||||
print(f"监听时发生错误: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 配置参数
|
||||
PORT = "/dev/ttyACM0" # 修改为你的串口号
|
||||
BAUD_RATE = 115200 # 修改为你的设备波特率
|
||||
HEX_COMMAND = {
|
||||
"Clockwise_rotation":"7B 01 02 01 20 0E 10 00 64 23 7D", # 顺时针旋转360
|
||||
"Counterclockwise_rotation":"7B 01 02 00 20 0E 10 00 64 22 7D",
|
||||
"One_revolution_per_second":"7B 01 01 00 20 00 00 00 3F 64 7D", # 有问题
|
||||
"Ten_radin_per_second":"7B 01 01 00 20 00 00 00 64 3F 7D",
|
||||
"Eight_radin_per_second": "7B 01 01 00 20 00 00 00 50 0B 7D",
|
||||
"stop":"7B 01 01 00 20 00 00 00 00 5B 7D"
|
||||
}# 要发送的指令
|
||||
|
||||
try:
|
||||
# 生成并发送开始旋转指令(速度 = 8)
|
||||
start_cmd = generate_fixed_command(start_speed)
|
||||
send_hex_command(PORT, BAUD_RATE, start_cmd)
|
||||
|
||||
# 延迟一段时间(比如 4 秒)
|
||||
time.sleep(4)
|
||||
|
||||
# 生成并发送停止指令(速度 = 0)
|
||||
stop_cmd = generate_fixed_command(stop_speed)
|
||||
send_hex_command(PORT, BAUD_RATE, stop_cmd)
|
||||
|
||||
print("电机控制流程执行完成。")
|
||||
|
||||
except Exception as e:
|
||||
print(f"运行错误: {e}")
|
||||
170
motor_control/motor_control_can_all.py
Normal file
170
motor_control/motor_control_can_all.py
Normal file
@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2025/7/18
|
||||
# @Author : hx
|
||||
# @File : motor_control_can_all.py
|
||||
'''
|
||||
|
||||
import can
|
||||
import time
|
||||
import argparse
|
||||
|
||||
|
||||
# 细分值映射表
|
||||
MICROSTEP_MAP = {
|
||||
2: 0x01,
|
||||
4: 0x02,
|
||||
8: 0x04,
|
||||
16: 0x0,
|
||||
32: 0x20
|
||||
}
|
||||
|
||||
# ========================================
|
||||
# CAN 发送函数(使用 socketcan)
|
||||
# ========================================
|
||||
def send_can_frame(data, can_id=0x01, channel='can0', bustype='socketcan'):
|
||||
"""
|
||||
发送CAN帧
|
||||
:param data: 7字节的列表
|
||||
:param can_id: CAN ID
|
||||
:param channel: CAN 通道
|
||||
:param bustype: 总线类型
|
||||
"""
|
||||
try:
|
||||
bus = can.interface.Bus(channel=channel, bustype=bustype)
|
||||
msg = can.Message(arbitration_id=can_id, data=data, is_extended_id=False)
|
||||
bus.send(msg)
|
||||
print(f"[发送CAN帧] ID={can_id:02X} 数据=[{', '.join(f'0x{x:02X}' for x in data)}]")
|
||||
bus.shutdown()
|
||||
except Exception as e:
|
||||
print(f"[发送失败] {e}")
|
||||
|
||||
|
||||
# ========================================
|
||||
# 模式1:速度控制模式 (0x01)
|
||||
# ========================================
|
||||
def generate_speed_command(direction, microstep, speed):
|
||||
"""
|
||||
生成速度控制指令
|
||||
:param direction: 方向 (0:逆时针, 1:顺时针)
|
||||
:param microstep: 细分值 (2, 4, 8, 16, 32)
|
||||
:param speed: 速度值 (单位: rad/s)
|
||||
"""
|
||||
direction_byte = 0x01 if direction == 1 else 0x00
|
||||
microstep_byte = MICROSTEP_MAP.get(microstep, 0x20)
|
||||
|
||||
raw_speed = int(speed * 10)
|
||||
speed_high = (raw_speed >> 8) & 0xFF
|
||||
speed_low = raw_speed & 0xFF
|
||||
|
||||
return [0x01, direction_byte, microstep_byte, 0x00, 0x00, speed_high, speed_low]
|
||||
|
||||
|
||||
# ========================================
|
||||
# 模式2:位置控制模式 (0x02)
|
||||
# ========================================
|
||||
def generate_position_command(direction, microstep, angle):
|
||||
"""
|
||||
生成位置控制指令
|
||||
:param direction: 方向 (0:逆时针, 1:顺时针)
|
||||
:param microstep: 细分值 (2, 4, 8, 16, 32)
|
||||
:param angle: 角度值 (单位: 度)
|
||||
"""
|
||||
direction_byte = 0x01 if direction == 1 else 0x00
|
||||
microstep_byte = MICROSTEP_MAP.get(microstep, 0x20)
|
||||
|
||||
raw_angle = int(angle * 10)
|
||||
pos_high = (raw_angle >> 8) & 0xFF
|
||||
pos_low = raw_angle & 0xFF
|
||||
|
||||
return [0x02, direction_byte, microstep_byte, pos_high, pos_low, 0x00, 0x64]
|
||||
|
||||
|
||||
# ========================================
|
||||
# 模式3:力矩控制模式 (0x03)
|
||||
# ========================================
|
||||
def generate_torque_command(direction, microstep, current):
|
||||
"""
|
||||
生成力矩控制指令
|
||||
:param direction: 方向 (0:逆时针, 1:顺时针)
|
||||
:param microstep: 细分值 (2, 4, 8, 16, 32)
|
||||
:param current: 电流值 (单位: mA)
|
||||
"""
|
||||
direction_byte = 0x01 if direction == 1 else 0x00
|
||||
microstep_byte = MICROSTEP_MAP.get(microstep, 0x20)
|
||||
|
||||
raw_current = int(current)
|
||||
current_high = (raw_current >> 8) & 0xFF
|
||||
current_low = raw_current & 0xFF
|
||||
|
||||
return [0x03, direction_byte, microstep_byte, current_high, current_low, 0x00, 0x64]
|
||||
|
||||
|
||||
# ========================================
|
||||
# 模式4:单圈绝对角度控制模式 (0x04)
|
||||
# ========================================
|
||||
def generate_absolute_angle_command(direction, microstep, angle, speed):
|
||||
"""
|
||||
生成单圈绝对角度控制指令
|
||||
:param direction: 方向 (0:逆时针, 1:顺时针)
|
||||
:param microstep: 细分值 (2, 4, 8, 16, 32)
|
||||
:param angle: 目标角度 (单位: 度)
|
||||
:param speed: 速度值 (单位: rad/s)
|
||||
"""
|
||||
direction_byte = 0x01 if direction == 1 else 0x00
|
||||
microstep_byte = MICROSTEP_MAP.get(microstep, 0x20)
|
||||
|
||||
raw_angle = int(angle * 10)
|
||||
pos_high = (raw_angle >> 8) & 0xFF
|
||||
pos_low = raw_angle & 0xFF
|
||||
|
||||
raw_speed = int(speed * 10)
|
||||
speed_high = (raw_speed >> 8) & 0xFF
|
||||
speed_low = raw_speed & 0xFF
|
||||
|
||||
|
||||
return [0x04, direction_byte, microstep_byte, pos_high, pos_low, speed_high, speed_low]
|
||||
|
||||
|
||||
# ========================================
|
||||
# 主函数(命令行调用)
|
||||
# ========================================
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="CAN电机控制程序,支持4种模式")
|
||||
parser.add_argument("--mode", type=int, choices=[1, 2, 3, 4],
|
||||
help="控制模式: 1=速度, 2=位置, 3=力矩, 4=绝对角度")
|
||||
parser.add_argument("--direction", type=int, choices=[0, 1], default=1,
|
||||
help="方向: 0=逆时针, 1=顺时针")
|
||||
parser.add_argument("--microstep", type=int, choices=[2, 4, 8, 16, 32], default=32,
|
||||
help="细分值: 2, 4, 8, 16, 32")
|
||||
parser.add_argument("--value", type=float, required=True,
|
||||
help="控制值(速度: rad/s, 位置/角度: 度, 力矩: mA)")
|
||||
parser.add_argument("--speed_rad_per_sec", type=float,
|
||||
help="速度值(仅用于绝对角度模式)")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
if args.mode == 1:
|
||||
cmd = generate_speed_command(args.direction, args.microstep, args.value)
|
||||
elif args.mode == 2:
|
||||
cmd = generate_position_command(args.direction, args.microstep, args.value)
|
||||
elif args.mode == 3:
|
||||
cmd = generate_torque_command(args.direction, args.microstep, args.value)
|
||||
elif args.mode == 4:
|
||||
if args.speed_rad_per_sec is None:
|
||||
raise ValueError("绝对角度模式需要提供速度参数 (--speed_rad_per_sec)")
|
||||
cmd = generate_absolute_angle_command(args.direction, args.microstep, args.value, args.speed_rad_per_sec)
|
||||
|
||||
print(f"生成CAN指令: [{', '.join(f'0x{x:02X}' for x in cmd)}]")
|
||||
send_can_frame(cmd)
|
||||
|
||||
except Exception as e:
|
||||
print(f"错误: {e}")
|
||||
|
||||
|
||||
# ========================================
|
||||
# 程序入口
|
||||
# ========================================
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
212
motor_control/motor_control_usb_all.py
Normal file
212
motor_control/motor_control_usb_all.py
Normal file
@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2025/7/18
|
||||
# @Author : hx
|
||||
# @File : motor_control_modes.py
|
||||
'''
|
||||
|
||||
import serial
|
||||
import time
|
||||
import argparse
|
||||
|
||||
|
||||
# ========================================
|
||||
# 通用发送函数
|
||||
# ========================================
|
||||
def send_hex_command(port, baudrate, hex_data):
|
||||
"""
|
||||
发送十六进制指令
|
||||
"""
|
||||
try:
|
||||
byte_data = bytes.fromhex(hex_data.replace(" ", ""))
|
||||
with serial.Serial(port, baudrate, timeout=1) as ser:
|
||||
print(f"已连接串口 {port}, 波特率 {baudrate}")
|
||||
ser.write(byte_data)
|
||||
print(f"已发送指令: {hex_data}")
|
||||
time.sleep(0.1)
|
||||
if ser.in_waiting > 0:
|
||||
response = ser.read(ser.in_waiting)
|
||||
print(f"收到响应: {response.hex().upper()}")
|
||||
except Exception as e:
|
||||
print(f"发送失败: {e}")
|
||||
|
||||
|
||||
# ========================================
|
||||
# 模式1:速度控制模式 (0x01)
|
||||
# 控制字节3 = 0x01
|
||||
# 第6~7字节为0
|
||||
# 第8~9字节:速度值(单位 rad/s,放大10倍)
|
||||
# ========================================
|
||||
def generate_speed_command(direction, microstep, speed):
|
||||
"""
|
||||
生成速度控制指令
|
||||
:param direction: 方向 (0:逆时针, 1:顺时针)
|
||||
:param microstep: 细分值 (2, 4, 8, 16, 32)
|
||||
:param speed: 速度值 (单位: rad/s)
|
||||
"""
|
||||
command = [0x7B, 0x01, 0x01, direction, microstep, 0x00, 0x00]
|
||||
|
||||
# 转速数据,单位为 rad/s,放大10倍传输
|
||||
raw_speed = int(speed * 10)
|
||||
high_byte = (raw_speed >> 8) & 0xFF
|
||||
low_byte = raw_speed & 0xFF
|
||||
command += [high_byte, low_byte]
|
||||
|
||||
# 计算 BCC 校验和(前9个字节异或)
|
||||
checksum = 0
|
||||
for b in command:
|
||||
checksum ^= b
|
||||
|
||||
full_command = command + [checksum, 0x7D]
|
||||
return ' '.join(f'{b:02X}' for b in full_command)
|
||||
|
||||
|
||||
# ========================================
|
||||
# 模式2:位置控制模式 (0x02)
|
||||
# 控制字节3 = 0x02
|
||||
# 第6~7字节:角度值(单位 度,放大10倍)
|
||||
# 第8~9字节为0
|
||||
# ========================================
|
||||
def generate_position_command(direction, microstep, angle):
|
||||
"""
|
||||
生成位置控制指令
|
||||
:param direction: 方向 (0:逆时针, 1:顺时针)
|
||||
:param microstep: 细分值 (2, 4, 8, 16, 32)
|
||||
:param angle: 角度值 (单位: 度)
|
||||
"""
|
||||
command = [0x7B, 0x01, 0x02, direction, microstep]
|
||||
|
||||
# 角度数据,单位为度,放大10倍传输
|
||||
raw_angle = int(angle * 10)
|
||||
high_byte = (raw_angle >> 8) & 0xFF
|
||||
low_byte = raw_angle & 0xFF
|
||||
command += [low_byte, high_byte, 0x00, 0x64]
|
||||
|
||||
# 计算 BCC 校验和(前9个字节异或)
|
||||
checksum = 0
|
||||
for b in command:
|
||||
checksum ^= b
|
||||
|
||||
full_command = command + [checksum, 0x7D]
|
||||
return ' '.join(f'{b:02X}' for b in full_command)
|
||||
|
||||
|
||||
# ========================================
|
||||
# 模式3:力矩控制模式 (0x03)
|
||||
# 控制字节3 = 0x03
|
||||
# 第6~7字节:电流值(单位 mA)
|
||||
# 第8~9字节为0
|
||||
# ========================================
|
||||
def generate_torque_command(direction, microstep, current):
|
||||
"""
|
||||
生成力矩控制指令
|
||||
:param direction: 方向 (0:逆时针, 1:顺时针)
|
||||
:param microstep: 细分值 (2, 4, 8, 16, 32)
|
||||
:param current: 电流值 (单位: mA)
|
||||
"""
|
||||
command = [0x7B, 0x01, 0x03, direction, microstep]
|
||||
|
||||
# 电流数据,单位为 mA
|
||||
raw_current = int(current)
|
||||
high_byte = (raw_current >> 8) & 0xFF
|
||||
low_byte = raw_current & 0xFF
|
||||
command += [low_byte, high_byte, 0x00, 0x64]
|
||||
|
||||
# 计算 BCC 校验和(前9个字节异或)
|
||||
checksum = 0
|
||||
for b in command:
|
||||
checksum ^= b
|
||||
|
||||
full_command = command + [checksum, 0x7D]
|
||||
return ' '.join(f'{b:02X}' for b in full_command)
|
||||
|
||||
|
||||
# ========================================
|
||||
# 模式4:单圈绝对角度控制模式 (0x04)
|
||||
# 控制字节3 = 0x04
|
||||
# 第6~7字节:目标角度(单位 度,放大10倍)
|
||||
# 第8~9字节为0
|
||||
# ========================================
|
||||
def generate_absolute_angle_command(direction, microstep, target_angle, speed_rad_per_sec):
|
||||
"""
|
||||
生成单圈绝对角度控制指令
|
||||
:param direction: 方向 (0:逆时针, 1:顺时针)
|
||||
:param microstep: 细分值 (2, 4, 8, 16, 32)
|
||||
:param target_angle: 目标角度 (单位: 度)
|
||||
:param speed_rad_per_sec: 速度 (单位: rad/s)
|
||||
:return: 十六进制指令字符串
|
||||
"""
|
||||
command = [0x7B, 0x01, 0x04, direction, microstep]
|
||||
|
||||
# 目标角度(放大10倍)
|
||||
raw_angle = int(target_angle * 10)
|
||||
pos_high = (raw_angle >> 8) & 0xFF
|
||||
pos_low = raw_angle & 0xFF
|
||||
command += [pos_high, pos_low]
|
||||
|
||||
# 速度(放大10倍)
|
||||
raw_speed = int(speed_rad_per_sec * 10)
|
||||
speed_high = (raw_speed >> 8) & 0xFF
|
||||
speed_low = raw_speed & 0xFF
|
||||
command += [speed_high, speed_low]
|
||||
|
||||
# 计算 BCC 校验和(前9个字节异或)
|
||||
checksum = 0
|
||||
for b in command:
|
||||
checksum ^= b
|
||||
|
||||
full_command = command + [checksum, 0x7D]
|
||||
return ' '.join(f'{b:02X}' for b in full_command)
|
||||
|
||||
|
||||
# ======================================1
|
||||
# 主函数(命令行调用)
|
||||
# ========================================
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="电机控制程序,支持4种模式")
|
||||
parser.add_argument("--port", default="/dev/ttyACM0", help="串口号")
|
||||
parser.add_argument("--baud", type=int, default=115200, help="波特率")
|
||||
parser.add_argument("--mode", type=int, choices=[1, 2, 3, 4],
|
||||
help="控制模式: 1=速度, 2=位置, 3=力矩, 4=绝对角度")
|
||||
parser.add_argument("--direction", type=int, choices=[0, 1], default=1,
|
||||
help="方向: 0=逆时针, 1=顺时针")
|
||||
parser.add_argument("--microstep", type=int, choices=[2, 4, 8, 16, 32], default=32,
|
||||
help="细分值: 2, 4, 8, 16, 32")
|
||||
parser.add_argument("--value", type=float, required=True,
|
||||
help="控制值(速度: rad/s, 位置/角度: 度, 力矩: mA)")
|
||||
parser.add_argument("--speed_rad_per_sec", type=float, required=True,
|
||||
help="控制值(速度)")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
if args.mode == 1:
|
||||
cmd = generate_speed_command(args.direction, args.microstep, args.value)
|
||||
elif args.mode == 2:
|
||||
cmd = generate_position_command(args.direction, args.microstep, args.value)
|
||||
elif args.mode == 3:
|
||||
cmd = generate_torque_command(args.direction, args.microstep, args.value)
|
||||
elif args.mode == 4:
|
||||
cmd = generate_absolute_angle_command(args.direction, args.microstep, args.value,args.speed_rad_per_sec)
|
||||
|
||||
print(f"生成指令: {cmd}")
|
||||
send_hex_command(args.port, args.baud, cmd)
|
||||
print("指令发送完成。")
|
||||
|
||||
except Exception as e:
|
||||
print(f"错误: {e}")
|
||||
|
||||
|
||||
# ========================================
|
||||
# 程序入口
|
||||
# ========================================
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
#python motor_control_usb_all.py --mode 1 --direction 1 --microstep 32 --value 10 --speed_rad_per_sec=10
|
||||
|
||||
#python motor_control_usb_all.py --mode 2 --direction 1 --microstep 32 --value 360.0 --speed_rad_per_sec=10
|
||||
|
||||
#python motor_control_usb_all.py --mode 3 --direction 1 --microstep 32 --value 1000 --speed_rad_per_sec=10
|
||||
|
||||
#python motor_control_usb_all.py --mode 4 --direction 1 --microstep 32 --value 100.0 --speed_rad_per_sec=10
|
||||
134
motor_control/motor_test_mostimprotant.py
Normal file
134
motor_control/motor_test_mostimprotant.py
Normal file
@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
# @Time : 2025/7/15 17:52
|
||||
# @Author : hx
|
||||
# @File : motor.py
|
||||
'''
|
||||
import serial
|
||||
import time
|
||||
import math
|
||||
|
||||
start_speed = 8
|
||||
stop_speed = 0
|
||||
|
||||
def send_hex_command(port, baudrate, hex_data):
|
||||
"""
|
||||
通过串口发送十六进制指令
|
||||
:param port: 串口号 (如 'COM3' 或 '/dev/ttyUSB0')
|
||||
:param baudrate: 波特率 (如 9600)
|
||||
:param hex_data: 十六进制字符串 (如 "7B 01 02 01 20 0E 10 00 64 23 7D")
|
||||
"""
|
||||
try:
|
||||
# 转换十六进制字符串为字节数据
|
||||
byte_data = bytes.fromhex(hex_data.replace(" ", ""))
|
||||
|
||||
# 打开串口
|
||||
with serial.Serial(port, baudrate, timeout=1) as ser:
|
||||
print(f"已连接串口 {port}, 波特率 {baudrate}")
|
||||
|
||||
# 发送指令
|
||||
ser.write(byte_data)
|
||||
print(f"已发送指令: {hex_data}")
|
||||
|
||||
# 可选:等待并读取返回数据
|
||||
time.sleep(0.1)
|
||||
if ser.in_waiting > 0:
|
||||
response = ser.read(ser.in_waiting)
|
||||
print(f"收到响应: {response.hex().upper()}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"发生错误: {e}")
|
||||
|
||||
def generate_fixed_command(speed):
|
||||
"""
|
||||
生成固定的速度控制指令: 7B 01 01 00 20 00 00 00 <speed> <checksum> 7D
|
||||
参数:
|
||||
speed (int): 十进制速度值 (0~255)
|
||||
返回:
|
||||
str: 十六进制命令字符串
|
||||
"""
|
||||
|
||||
actual_speed = int(speed * 10)
|
||||
|
||||
if not (0 <= actual_speed <= 255):
|
||||
raise ValueError("速度必须在 0~255 范围内")
|
||||
|
||||
command_bytes = [
|
||||
0x7B, 0x01, 0x01, # 帧头、地址、控制模式
|
||||
0x00, 0x20, # 方向、细分
|
||||
0x00, 0x00, # 位置高/低字节(持续旋转)
|
||||
0x00, actual_speed # 保持整数速度值
|
||||
]
|
||||
|
||||
# 计算校验和(前9个字节异或)
|
||||
checksum = 0
|
||||
for byte in command_bytes:
|
||||
checksum ^= byte
|
||||
|
||||
full_command = command_bytes + [checksum, 0x7D]
|
||||
return ' '.join(f'{byte:02X}' for byte in full_command)
|
||||
|
||||
def listen_and_respond(port, baudrate):
|
||||
"""
|
||||
持续监听串口,一旦有信号就执行开始和停止的指令
|
||||
"""
|
||||
try:
|
||||
with serial.Serial(port, baudrate, timeout=1) as ser:
|
||||
ser.reset_input_buffer()
|
||||
print(f"[监听中] 串口 {port} 已打开,等待信号...")
|
||||
|
||||
while True:
|
||||
if ser.in_waiting > 0:
|
||||
incoming = ser.read(ser.in_waiting)
|
||||
print(f"[收到信号] 内容: {incoming.hex().upper()}")
|
||||
|
||||
# 执行开始旋转
|
||||
start_cmd = generate_fixed_command(8)
|
||||
print("发送开始指令:", start_cmd)
|
||||
ser.write(bytes.fromhex(start_cmd.replace(" ", "")))
|
||||
|
||||
# 延迟一段时间
|
||||
time.sleep(4)
|
||||
|
||||
# 执行停止
|
||||
stop_cmd = generate_fixed_command(0)
|
||||
print("发送停止指令:", stop_cmd)
|
||||
ser.write(bytes.fromhex(stop_cmd.replace(" ", "")))
|
||||
|
||||
print("[已完成一次响应]\n等待下一次信号...\n")
|
||||
|
||||
time.sleep(0.1) # 避免CPU占用过高
|
||||
|
||||
except Exception as e:
|
||||
print(f"监听时发生错误: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 配置参数
|
||||
PORT = "/dev/ttyACM0" # 修改为你的串口号
|
||||
BAUD_RATE = 115200 # 修改为你的设备波特率
|
||||
HEX_COMMAND = {
|
||||
"Clockwise_rotation":"7B 01 02 01 20 0E 10 00 64 23 7D", # 顺时针旋转360
|
||||
"Counterclockwise_rotation":"7B 01 02 00 20 0E 10 00 64 22 7D",
|
||||
"One_revolution_per_second":"7B 01 01 00 20 00 00 00 3F 64 7D", # 有问题
|
||||
"Ten_radin_per_second":"7B 01 01 00 20 00 00 00 64 3F 7D",
|
||||
"Eight_radin_per_second": "7B 01 01 00 20 00 00 00 50 0B 7D",
|
||||
"stop":"7B 01 01 00 20 00 00 00 00 5B 7D"
|
||||
}# 要发送的指令
|
||||
|
||||
try:
|
||||
# 生成并发送开始旋转指令(速度 = 8)
|
||||
start_cmd = generate_fixed_command(start_speed)
|
||||
send_hex_command(PORT, BAUD_RATE, start_cmd)
|
||||
|
||||
# 延迟一段时间(比如 4 秒)
|
||||
time.sleep(4)
|
||||
|
||||
# 生成并发送停止指令(速度 = 0)
|
||||
stop_cmd = generate_fixed_command(stop_speed)
|
||||
send_hex_command(PORT, BAUD_RATE, stop_cmd)
|
||||
|
||||
print("电机控制流程执行完成。")
|
||||
|
||||
except Exception as e:
|
||||
print(f"运行错误: {e}")
|
||||
9
motor_control/test.py
Normal file
9
motor_control/test.py
Normal file
@ -0,0 +1,9 @@
|
||||
import time
|
||||
|
||||
print("守护进程已启动,将持续运行...")
|
||||
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("守护进程已停止")
|
||||
Reference in New Issue
Block a user