完整代码
import typingfrom PyQt5 import QtCoreimport rclpyfrom rclpy.node import Nodefrom example_interfaces.srv import AddTwoIntsfrom PyQt5.QtWidgets import QWidget,QApplication,QFormLayout,QLineEdit,QLabel,QPushButtonfrom PyQt5.QtCore import QTimerimport sysfrom turtlesim.msg import Posefrom geometry_msgs.msg import Twistfrom std_msgs.msg import Float64from threading import Threadimport mathimport timeclass MainWindow(QWidget): def __init__(self, node) -> None: super().__init__() # 节点 self.node = node # 初始化ui界面 self.init_ui() # 定时器 timer = QTimer(self) timer.setInterval(20) timer.start() timer.timeout.connect(self.onUpdate) # 当前的x y theta self.curX = 0 self.curY = 0 self.curTheta = 0 # 订阅位置 self.sub = self.node.create_subscription(Pose,'/turtle1/pose',self.pose_callback,10) # 小乌龟控制发布者 self.control_pub = self.node.create_publisher(Twist,'/turtle1/cmd_vel',10) # 发布小乌龟速度 self.speed_pub = self.node.create_publisher(Float64,'/turtle/speed',10) def pose_callback(self,pose): self.curX = pose.x self.curY = pose.y self.curTheta = pose.theta # 设置当前位置 self.xL.setText(str(self.curX)) self.yL.setText(str(self.curY)) self.thetaL.setText(str(self.curTheta)) # 定时更新 def onUpdate(self): if not rclpy.ok(): self.close() return rclpy.spin_once(self.node) def init_ui(self): # 布局 layout = QFormLayout() self.setLayout(layout) self.targetEditX = QLineEdit() self.targetEditY = QLineEdit() self.xL = QLabel() self.yL = QLabel() self.thetaL = QLabel() self.kpE = QLineEdit() self.kiE = QLineEdit() self.kdE = QLineEdit() executeBtn = QPushButton('执行') vSweepBtn = QPushButton('纵向扫地') layout.addRow('目标X:',self.targetEditX) layout.addRow('目标Y:',self.targetEditY) layout.addRow('当前X:',self.xL) layout.addRow('当前Y:',self.yL) layout.addRow('当前theta:',self.thetaL) layout.addRow('KP:',self.kpE) layout.addRow('KI:',self.kiE) layout.addRow('KD:',self.kdE) self.kpE.setText('0.1') self.kiE.setText('0.1') self.kdE.setText('0.1') self.targetEditX.setText('8.544444561') self.targetEditY.setText('5.544444561') layout.addRow('',executeBtn) layout.addRow('',vSweepBtn) executeBtn.clicked.connect(self.move) vSweepBtn.clicked.connect(self.vSweep) def vSweep(self): print('zhixing1') Thread(target=self.vSweepImpl).start() def vSweepImpl(self): print('zhixing2') for i in range(1,11): self.executeMove(i,1) self.executeMove(i,10) def distance(self,curX,curY,targetX,targetY): return math.sqrt(pow(targetX-curX,2)+pow(targetY-curY,2)) def caclAngle(self,curX,curY,targetX,targetY,theta): angle = math.atan2(targetY-curY,targetX-curX)-theta if angle>math.pi: angle -= 2*math.pi elif angle<-math.pi: angle += 2*math.pi return angle def executeMove(self,targetX,targetY): dst = self.distance(self.curX,self.curY,targetX,targetY) # 定义时间 control_time = 5.0 # 掉头时间 turnTime = control_time/12 # 睡眠 hz = 10 # 频率 rate = self.node.create_rate(hz) # 当前速度 curSpeed = 0 # 上一次的速度差 lastSpeedDiff = 0 # 累计误差 totalSpeedDiff = 0 # kp ki kd kp = float(self.kpE.text()) ki = float(self.kiE.text()) kd = float(self.kdE.text()) # 求速度 while self.distance(self.curX,self.curY,targetX,targetY)>0.1: # 目标速度 speed = self.distance(self.curX,self.curY,targetX,targetY)/control_time # kp curSpeedDiff = speed-curSpeed curSpeed += curSpeedDiff*kp # kd curSpeed += (curSpeedDiff-lastSpeedDiff)*kd lastSpeedDiff = curSpeedDiff # ki totalSpeedDiff += curSpeedDiff curSpeed += totalSpeedDiff*ki # 发布控制小乌龟 data = Twist() data.linear.x = curSpeed data.angular.z = self.caclAngle(self.curX,self.curY,targetX,targetY,self.curTheta)/turnTime self.control_pub.publish(data) # 发布速度plot显示 speed_data = Float64() speed_data.data = curSpeed self.speed_pub.publish(speed_data) rate.sleep() control_time -= 1.0/hz # time.sleep(0.09) print('stop') # 发布控制小乌龟 data = Twist() data.linear.x = 0.0 self.control_pub.publish(data) def move(self): targetX = float(self.targetEditX.text()) targetY = float(self.targetEditY.text()) # 开启线程执行 Thread(target=self.executeMove,args=(targetX,targetY)).start()def main(): rclpy.init() node = Node('service_server_node') app = QApplication(sys.argv) window = MainWindow(node) window.show() print('app start') result = app.exec_() node.destroy_node() rclpy.shutdown() sys.exit(result)if __name__ == '__main__': main()