完整代码
import typing
from PyQt5 import QtCore
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
from PyQt5.QtWidgets import QWidget,QApplication,QFormLayout,QLineEdit,QLabel,QPushButton
from PyQt5.QtCore import QTimer
import sys
from turtlesim.msg import Pose
from geometry_msgs.msg import Twist
from std_msgs.msg import Float64
from threading import Thread
import math
import time
class 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()