写不NG流程:添加传送带运行、步进电机移动、线条个数方便后续夹爪的抓取、气动夹爪控制流程

This commit is contained in:
2026-01-09 17:52:52 +08:00
parent 8821b30689
commit a8a35545cc
7 changed files with 410 additions and 43 deletions

View File

@ -112,8 +112,8 @@ class DMMotorController:
:param v_desired: 目标速度rad/s, 范围[-30, 30] :param v_desired: 目标速度rad/s, 范围[-30, 30]
""" """
try: try:
# 归零 + 发送运动指令 # 发送运动指令
self.motor_control.set_zero_position(self.motor) # self.motor_control.set_zero_position(self.motor)
self.motor_control.control_Pos_Vel(self.motor, p_desired, v_desired) self.motor_control.control_Pos_Vel(self.motor, p_desired, v_desired)
time.sleep(0.1) time.sleep(0.1)
print(f"✅ 位置-速度控制:位置={p_desired}rad | 速度={v_desired}rad/s") print(f"✅ 位置-速度控制:位置={p_desired}rad | 速度={v_desired}rad/s")
@ -198,6 +198,7 @@ class DMMotorController:
except Exception as e: except Exception as e:
print(f"修改电机限位参数失败: {str(e)}") print(f"修改电机限位参数失败: {str(e)}")
def __del__(self): def __del__(self):
"""析构函数:确保程序退出时失能电机、关闭串口""" """析构函数:确保程序退出时失能电机、关闭串口"""
try: try:
@ -211,7 +212,6 @@ class DMMotorController:
except Exception as e: except Exception as e:
print(f" 析构函数执行警告:{str(e)}") print(f" 析构函数执行警告:{str(e)}")
def dm_motor_control(): def dm_motor_control():
# 1.创建电机控制器实例 # 1.创建电机控制器实例
motor_controller = DMMotorController( motor_controller = DMMotorController(

View File

@ -100,6 +100,7 @@ class RelayController:
self.press_sensors_thread = None # 保存按压开关线程对象 self.press_sensors_thread = None # 保存按压开关线程对象
self.press_sensors_monitor_running = False # 按压传感器监听线程运行标志 self.press_sensors_monitor_running = False # 按压传感器监听线程运行标志
self.last_press_sensor_status = False # 记录传感器上一次状态,初始为无信号(用于上升沿检测)
def send_command(self, command): def send_command(self, command):
""" """
@ -291,12 +292,16 @@ class RelayController:
# 检测两个传感器任意一个是否触发 # 检测两个传感器任意一个是否触发
press_sensor1_status = self.get_device_status(PRESS_SENSOR1, 'sensors') press_sensor1_status = self.get_device_status(PRESS_SENSOR1, 'sensors')
press_sensor2_status = self.get_device_status(PRESS_SENSOR2, 'sensors') press_sensor2_status = self.get_device_status(PRESS_SENSOR2, 'sensors')
if press_sensor1_status or press_sensor2_status: current_sensor_state = press_sensor1_status or press_sensor2_status
# 上升沿触发(仅从无信号-->有信号时,才触发事件)
if current_sensor_state and not self.last_press_sensor_status:
press_sensors_triggered.set() # 触发事件,通知主线程 press_sensors_triggered.set() # 触发事件,通知主线程
logging.info("双压传感器触发:线条已落到传送带") logging.info("双压传感器触发:线条已落到传送带")
# 重置事件(等待下一次触发)
time.sleep(0.1) # 防重复触发 # 更新上一次传感器状态,为下一次上升沿检测做准备
press_sensors_triggered.clear() self.last_press_sensor_status = current_sensor_state
# 传感器检测间隔
time.sleep(check_interval) time.sleep(check_interval)
except Exception as e: except Exception as e:
logging.info(f"双压传感器监听异常: {e}") logging.info(f"双压传感器监听异常: {e}")

119
RK1106/RK1106_server.py Normal file
View File

@ -0,0 +1,119 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
# @Time : 2026/1/9 10:45
# @Author : reenrr
# @File : RK1106_server.py
# @Desc : RK1106服务端等待工控机调用
'''
import socket
import logging
import sys
from test import motor_demo
# --------日志配置(终端+文件双输出)--------------
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
# 核心新增:日志文件配置
handlers=[
# 1. 文件处理器:保存到.log文件
logging.FileHandler(
"RK1106_server.log", # Buildroot推荐路径临时测试可改/tmp/1106_server.log
mode='a', # 追加模式(不会覆盖历史日志)
encoding='utf-8' # 防止中文乱码(必加)
),
# 2. 终端处理器:输出到控制台
logging.StreamHandler(sys.stdout)
]
)
# --------配置TCP服务端----------
HOST = "127.0.0.1"
PORT = 8888
# 程序映射表(指令表示 -> 执行函数)
PROG_MAP = {
# "STEPPER_TEST": motor_test_demo,
"test": motor_demo
}
def parse_command(cmd_str: str) ->tuple[str, dict]:
"""
解析工控机发送的指令字符串
:param cmd_str: 指令字符串
:return: 指令名称,参数字典
"""
# 空指令处理
if not cmd_str or cmd_str.strip() == "":
return "", {}
# 分割指令标识和参数
cmd_parts = cmd_str.strip().split("|", 1)
prog_id = cmd_parts[0].strip()
params = {}
# 解析参数格式param1=val1&param2=val2
if len(cmd_parts) > 1 and cmd_parts[1].strip() != "":
param_str = cmd_parts[1].strip()
param_pairs = param_str.split("&")
for pair in param_pairs:
if "=" in pair:
key, value = pair.split("=", 1) # 处理值中含=的情况
# 类型自动转换(数字/字符串)
try:
params[key.strip()] = int(value.strip())
except ValueError:
try:
params[key.strip()] = float(value.strip())
except ValueError:
params[key.strip()] = value.strip()
return prog_id, params
# ----------对外接口----------
def server():
# 创建TCP socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
# 允许端口复用
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen(1) # 只允许1个工控机连接
logging.info(f"[1106] 服务已启动,监听端口:{PORT},等待工控机连接...")
# 等待工控机连接
conn, addr = server_socket.accept()
with conn:
logging.info(f"[1106] 工控机已连接:{addr}")
# 循环接收指令
while True:
# 接收指令最大1024字节
data = conn.recv(1024).decode()
logging.info(f"\n[1106] 收到工控机指令:{data}")
# 解析指令
prog_id, params = parse_command(data)
logging.info(f"[1106] 解析结果 - 指令:{prog_id},参数:{params}")
# 执行对应程序
responses = ""
if prog_id in PROG_MAP:
try:
result = PROG_MAP[prog_id](**params)
response = f"SUCCESS|步进电机测试执行完成,结果:{result}"
logging.info(f"[1106] {response}")
except Exception as e:
response = f"FAIL|步进电机测试执行失败:{str(e)}"
logging.error(f"[1106] {response}", exc_info=True)
else:
response = f"FAIL|未知指令:{prog_id},支持指令:{list(PROG_MAP.keys())}"
logging.warning(f"[1106] {response}")
# 发送响应给工控机
conn.sendall(response.encode("utf-8"))
logging.info(f"[1106] 已发送响应:{response}")
# ----------测试接口----------
if __name__ == "__main__":
server()

View File

@ -249,7 +249,7 @@ class StepperMotor:
"""析构函数:确保资源释放""" """析构函数:确保资源释放"""
self.close() self.close()
# 使用示例 # ----------对外接口-----------
def motor_demo(): def motor_demo():
"""电机控制示例""" """电机控制示例"""
motor = None motor = None
@ -287,6 +287,5 @@ def motor_demo():
motor.close() motor.close()
print("[OK] 程序退出完成") print("[OK] 程序退出完成")
if __name__ == '__main__': if __name__ == '__main__':
motor_demo() motor_demo()

125
client.py Normal file
View File

@ -0,0 +1,125 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# @Time : 2026/1/9 10:49
# @Author : reenrr
# @File : client.py
# @Desc : 工具工控机客户端
"""
import socket
import logging
import time
# -------- RK1106配置 ----------
_1106_IP = "127.0.0.1" # 替换为3506的真实IP本地测试用127.0.0.1
_1106_PORT = 8888 # 和3506服务端一致的端口
TIMEOUT = 10 # 通讯超时时间(秒)
class IndustrialPCClient:
"""工控机客户端类封装与1106的通讯逻辑"""
def __init__(self, ip: str, port: int, timeout: int = 10):
self.ip = ip
self.port = port
self.timeout = timeout
self.socket = None # 通讯socket
def connect(self) -> bool:
"""连接1106服务端"""
try:
# 创建TCP socket
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(self.timeout) # 设置超时
# 连接3506
self.socket.connect((self.ip, self.port))
logging.info(f"[工控机] 成功连接1106{self.ip}:{self.port}")
return True
except socket.timeout:
logging.error(f"[工控机] 连接1106超时{self.timeout}s")
self.close()
return False
except ConnectionRefusedError:
logging.error(f"[工控机] 1106拒绝连接请检查1106服务是否启动")
self.close()
return False
except Exception as e:
logging.error(f"[工控机] 连接1106失败{str(e)}", exc_info=True)
self.close()
return False
def send_command(self, prog_id: str, **params) -> tuple[bool, str]:
"""
向1106发送指令
:param prog_id: 指令标识如STEPPER_TEST
:param params: 指令参数如speed=50, steps=200
:return: (是否成功, 响应信息)
"""
if not self.socket:
return False, "未连接1106请先调用connect()"
# 构造指令字符串符合1106解析格式
param_str = "&".join([f"{k}={v}" for k, v in params.items()])
cmd = f"{prog_id}|{param_str}" if param_str else prog_id
try:
# 发送指令UTF-8编码
self.socket.sendall(cmd.encode("utf-8"))
logging.info(f"[工控机] 发送指令:{cmd}")
# 接收3506响应最大1024字节
resp = self.socket.recv(1024).decode("utf-8", errors="ignore").strip()
if not resp:
return False, "未收到1106响应"
# 解析响应SUCCESS/FAIL|描述)
if "|" in resp:
status, msg = resp.split("|", 1)
success = status == "SUCCESS"
logging.info(f"[工控机] 1106响应{'成功' if success else '失败'} - {msg}")
return success, msg
else:
# 响应格式异常
logging.warning(f"[工控机] 1106响应格式异常{resp}")
return False, f"响应格式错误:{resp}"
except socket.timeout:
logging.error(f"[工控机] 发送指令后超时({self.timeout}s未收到响应")
return False, "通讯超时"
except Exception as e:
logging.error(f"[工控机] 发送指令失败:{str(e)}", exc_info=True)
return False, str(e)
def close(self):
"""关闭连接"""
if self.socket:
self.socket.close()
self.socket = None
logging.info("[工控机] 已断开与1106的连接")
# ---------对外接口----------
def control_stepper_motor():
"""示例工控机调用1106的步进电机控制程序"""
# 初始化客户端
client = IndustrialPCClient(_1106_IP, _1106_PORT, TIMEOUT)
# 连接1106
if not client.connect():
return
try:
# ========== 示例1无参数调用步进电机测试 ==========
logging.info("\n===== 无参数调用步进电机测试 =====")
success, msg = client.send_command("test")
if not success:
logging.error(f"调用失败:{msg}")
time.sleep(2) # 间隔2秒 可能根据实际效果进行延时调整
finally:
# 确保关闭连接
client.close()
# ---------测试接口----------
if __name__ == "__main__":
control_stepper_motor()

View File

@ -11,7 +11,7 @@ import threading
import time import time
from datetime import datetime from datetime import datetime
import serial import serial
from EMV import RelayController from EMV.EMV_test import RelayController
logging.getLogger("pymodbus").setLevel(logging.CRITICAL) logging.getLogger("pymodbus").setLevel(logging.CRITICAL)

View File

@ -1,19 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' """
# @Time : 2025/12/12 11:05 # @Time : 2025/12/12 11:05
# @Author : reenrr # @Author : reenrr
# @File : main_control.py # @File : main_control.py
# @Desc : 主控程序 # @Desc : 主控程序
''' """
import multiprocessing # 多进程模块
import threading
from threading import Event
import time import time
from EMV.EMV_test import press_sensors_triggered, control_solenoid, global_relay from EMV.EMV_test import press_sensors_triggered, control_solenoid, global_relay
from visual_algorithm.visual_algorithm import flaw_detection from visual_algorithm.visual_algorithm import flaw_detection
import logging import logging
import os import os
from conveyor_controller.conveyor_master_controller2_test import conveyor_control
from client import IndustrialPCClient
from DM.DM_Motor_test import DMMotorController, Control_Type, dm_motor_control
# ------------ 日志+参数配置 ------------ # ------------ 日志+参数配置 ------------
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
@ -29,59 +29,178 @@ logging.basicConfig(
] ]
) )
# ------------全局事件------------- # -------------------------- 电机参数配置 --------------------------
manger = multiprocessing.Manager() SLAVE_ID = 0x01
conveyor_start_event = manger.Event() MASTER_ID = 0x11
PORT = 'COM10'
BAUDRATE = 921600
def start_thread(): class LineMainControlSystem:
"""开启各种线程""" """
global_relay.start_press_sensors_monitor() # 双压传感器监听线程 线条主控系统类
"""
def __init__(self):
"""
构造方法:初始化类属性
"""
# 客户端
self.client = IndustrialPCClient(ip='127.0.0.1', port=8888, timeout=10)
# 连接服务端
if not self.client.connect():
return
def stop_thread(): # 合格线条处理记数
"""关闭各种线程""" self.qualified_line_count = 0
global_relay.stop_press_sensors_monitor() # 双压传感器监听线程 self.qualified_trigger_threshold = 5
# 创建达妙电机实例、参数
self.motor_controller = DMMotorController(
slave_id=SLAVE_ID,
master_id=MASTER_ID,
port=PORT,
baudrate=BAUDRATE
)
self.dm_motor_init()
def quality_testing(): def dm_motor_init(self):
logging.info("线条开始质量检测:") # 切换到位置-速度模式
self.motor_controller.switch_control_mode(Control_Type.POS_VEL)
# 执行质量检测 # 使能电机
result = flaw_detection({"line_id": "L001", "straightness": 0.95, "noise_ratio": 0.08}) self.motor_controller.enable_motor()
if result == "qualified":
# 将电机位置恢复到初始位置(防止断电,电机停在某个位置上)
dm_motor_position = self.motor_controller.get_position()
logging.info(f"初始位置{dm_motor_position}")
# 将位置移动到0位
self.motor_controller.control_pos_vel(p_desired=0, v_desired=30)
time.sleep(10)
def start_thread(self):
"""开启各种线程"""
logging.info("开启各类监听线程")
global_relay.start_press_sensors_monitor() # 双压传感器监听线程
def stop_thread(self):
"""关闭各种线程"""
logging.info("关闭各类监听线程")
global_relay.stop_press_sensors_monitor() # 双压传感器监听线程
def handle_line(self):
try:
logging.info("线条开始质量检测:")
# 执行质量检测
result = flaw_detection({"line_id": "L001", "straightness": 0.95, "noise_ratio": 0.08})
if result == "qualified":
self._handle_qualified_line()
elif result == "unqualified":
self._handle_unqualified_line()
except Exception as e:
logging.error(f"质量检测失败:{e}", exc_info=True)
finally:
# 关闭连接服务端
if self.client:
self.client.close()
logging.info("关闭连接服务端")
def _handle_qualified_line(self):
"""
处理合格线条的流程
"""
result = "合格" result = "合格"
logging.info("该线条是否合格:", result) logging.info(f"该线条是否合格:{result}")
logging.info("等待线条落到传送带(双压传感器触发)...") logging.info("等待线条落到传送带(双压传感器触发)...")
# 等待时间触发超时时间设为10秒避免无限等待 # 等待时间触发超时时间设为10秒避免无限等待
if press_sensors_triggered.wait(timeout=10): if press_sensors_triggered.wait(timeout=10):
press_sensors_triggered.clear()
logging.info("线条已落到传送带,控制两个传送带向前移动") logging.info("线条已落到传送带,控制两个传送带向前移动")
# 触发传送带启动事件 # 传送带运行
conveyor_start_event.set() conveyor_control()
# 验证传送带运行结果
press_sensor1_status = global_relay.get_device_status('press_sensor1', 'sensors')
press_sensor2_status = global_relay.get_device_status('press_sensor2', 'sensors')
if not press_sensor1_status and not press_sensor2_status:
logging.info("传送带运行正常,线条已移动")
self.qualified_line_count += 1
logging.info(f"已处理合格线条{self.qualified_line_count}/{self.qualified_trigger_threshold}")
# 控制步进电机移动
logging.info("控制步进电机移动")
success, msg = self.client.send_command("test")
if not success:
logging.error(f"调用失败:{msg}")
else:
logging.info("步进电机移动成功")
# 判断线条个数
if self.qualified_line_count >= self.qualified_trigger_threshold:
# 舵机、电机相关控制
self.qualified_line_count = 0
else:
logging.warning("传送带运行异常,线条未正常移动,不更新合格计数")
else: else:
logging.info("超时警告:线条未落到传送带,请检查") logging.info("线条未落到传送带,请检查传感器或设备状态")
elif result == "unqualified":
def _handle_unqualified_line(self):
"""
处理不合格线条的流程
"""
result = "不合格" result = "不合格"
logging.info("该线条是否合格:", result) logging.info("该线条是否合格:", result)
logging.info("等待线条落到传送带上") logging.info("等待线条落到传送带上")
# 等待时间触发超时时间设为10秒避免无限等待 # 等待时间触发超时时间设为10秒避免无限等待
if press_sensors_triggered.wait(timeout=10): if press_sensors_triggered.wait(timeout=10):
press_sensors_triggered.clear()
logging.info("线条已落到传送带") logging.info("线条已落到传送带")
# 执行NG动作控制电磁阀
logging.info("进入NG动作") logging.info("进入NG动作")
control_solenoid() # 执行NG动作,控制电磁阀 control_solenoid() # 执行NG动作,控制电磁阀
logging.info("NG动作结束") logging.info("NG动作结束")
# logging.info("判断NG线条是否落入肥料区")
def gripper_control(self):
"""
气动夹爪相关控制:达妙电机的移动、舵机的翻转
"""
# 打开电磁阀3
global_relay.open('solenoid_valve3', "devices")
# 控制达妙电机收缩
self.motor_controller.control_pos_vel(p_desired=282.6, v_desired=30) # 450mm 665-215
time.sleep(10)
# -----------对外接口------------- # -----------对外接口-------------
def main_control(): def main_control():
logging.info("开启各种线程") main_control_system = LineMainControlSystem()
start_thread()
logging.info("开始摆放线条") try:
logging.info("开启各种线程")
main_control_system.start_thread()
# 质量检测 while True: # 防止跳出循环
quality_testing() logging.info("开始摆放线条")
# 质量检测
main_control_system.handle_line()
except KeyboardInterrupt:
logging.info("用户手动退出")
finally:
# 程序退出时,关闭各类线程
main_control_system.stop_thread()
logging.info("主控程序已正常退出")
while True: # 防止跳出循环
time.sleep(1)
# ------------测试接口------------- # ------------测试接口-------------
if __name__ == '__main__': if __name__ == '__main__':