当前位置:   article > 正文

Vrep+Python实现小车运动仿真_v-rep pro edu v3.5.0搭建agv仿真

v-rep pro edu v3.5.0搭建agv仿真

环境安装以及配置:

本文用来记录自己使用Python远程控制Vrep进行两轮小车,进行差速运动仿真。模拟小车沿二维码运动

Vrep配置

       Vrep官网下载速度比较慢这里推荐网盘下载coppeliasim/vrep官网软件安装包(免费百度网盘链接)-CSDN博客

我ubuntu系统版本用的18.04,这里下载的是4.0版本的,经过测试最新的4.5版本在进行多台AGV控制的时候,出现了无法获取句柄。而且网上资料也多是旧版本的。

下载完之后解压,并配置环境

  1. echo 'export VREP_ROOT="$HOME/V-REP_PRO_EDU_V3_5_0_Linux"' >> ~/.bashrc
  2. source ~/.bashrc

ubuntu使用命令vrep打开软件

python配置:

需要在安装目录导出3个文件

如果Vrep3.6版本

1.找到 vrep.py和 vrepConst.py两个文件

安装目录/programming/remoteApiBindings/python/python

2.找到编译文件

如果是ubuntu系统找到 remoteApi.so

安装目录/programming/remoteApiBindings/lib/lib/Linux/64Bit

如果是window系统找到 remoteApi.dll

安装目录/programming/remoteApiBindings/lib/lib/Windows/64Bit

如果Vrep4.0版本

1.找到 sim.py 和 simConst.py 两个文件

 安装目录/programming/legacyRemoteApi/remoteApiBindings/python/python

2.找到编译文件

如果是ubuntu系统找到 remoteApi.so

安装目录/programming/legacyRemoteApi/remoteApiBindings/lib/lib/Ubuntu18_04

如果是window系统找到 remoteApi.dll

安装目录/programming/legacyRemoteApi/remoteApiBindings/lib/lib/Windows

将3个文件和你的脚本放在一起


Vrep软件操作:

打开软件后在左下角可以拉取模型

建模过程不做演示了,具体建模可以在B站看视频学习,我这里弄了12辆小车,地上的红色方块是建立的平面,用来模拟地面贴的二维码

在信息栏中可以看到小车的属性,python是用过这些属性对小车进行句柄控制

1.设置连接端口

  1. class VREP():
  2. def __init__(self) -> None:
  3. self.lock = threading.Lock()
  4. self.linear_velocity = 1
  5. self.speed = 0.026781272888183594 # m/s
  6. try:
  7. vrep.simxFinish(-1) # 关掉之前连接
  8. self.clientID = vrep.simxStart(
  9. '127.0.0.1', 19997, True, True, 5000, 5) # Connect to CoppeliaSim
  10. if self.clientID != -1:
  11. print('connect successfully')
  12. else:
  13. # Terminar este script
  14. sys.exit("[Error]: no se puede conectar")
  15. except:
  16. print('[Error]: Check if CoppeliaSim is open')
  17. self.running = False
  18. # self.start_sim()
'
运行

2.设置小车属性,多台小车名字需要不同,与代码中的小车名字要一致

每辆小车都有一个脚本,需要将脚本的disable打钩,否则在使用脚本控制的时候会发生冲突,启动仿真的时候小车会自动前进

更改小车轮子属性

在python脚本中使用simxGetObjectHandle函数获取句柄

获取小车和左右轮的句柄

  1. def creat_agv(self, i):
  2. """
  3. return agv = (agv1_handle,leftMotor1_handle, rightMotor1_handle)
  4. """
  5. _agv_name = "Pioneer_p3dx#"+str(i)
  6. _left_motor = "Pioneer_p3dx_leftMotor#"+str(i)
  7. _right_motor = "Pioneer_p3dx_rightMotor#"+str(i)
  8. agv = (self.get_handle(_agv_name),
  9. self.get_handle(_left_motor), self.get_handle(_right_motor))
  10. return agv
'
运行
  1. def get_handle(self, name):
  2. with self.lock:
  3. e, handle = vrep.simxGetObjectHandle(
  4. self.clientID, name, vrep.simx_opmode_oneshot_wait)
  5. if e != 0:
  6. print("[Error]: get {} handle error !!!".format(name))
  7. return handle
'
运行

至此Vrep相关配置已经完成。

Python接口介绍:

写一个vrep_client.py和导入的3个脚本放在一起

  1. from . import vrep
  2. import threading
  3. import time
  4. import numpy as np
  5. import sys
  6. class VREP():
  7. def __init__(self) -> None:
  8. self.lock = threading.Lock()
  9. self.linear_velocity = 1
  10. self.speed = 0.026781272888183594 # m/s
  11. try:
  12. vrep.simxFinish(-1) # 关掉之前连接
  13. self.clientID = vrep.simxStart(
  14. '127.0.0.1', 19997, True, True, 5000, 5) # Connect to CoppeliaSim
  15. if self.clientID != -1:
  16. print('connect successfully')
  17. else:
  18. # Terminar este script
  19. sys.exit("[Error]: no se puede conectar")
  20. except:
  21. print('[Error]: Check if CoppeliaSim is open')
  22. self.running = False
  23. # self.start_sim()
  24. def __del__(self):
  25. # if self.clientID:
  26. # 断开与V-REP仿真的连接
  27. self.running = False
  28. vrep.simxStopSimulation(
  29. self.clientID, vrep.simx_opmode_blocking) # 关闭仿真
  30. vrep.simxFinish(self.clientID)
  31. time.sleep(1) # 仿真开启延时5s
  32. vrep.simxFinish(-1) # 关闭连接
  33. def start_sim(self):
  34. with self.lock:
  35. res = vrep.simxStartSimulation(
  36. self.clientID, vrep.simx_opmode_oneshot_wait)
  37. if res == vrep.simx_return_ok:
  38. self.running = True
  39. print("仿真环境已启动")
  40. else:
  41. print("[Error]: 仿真环境启动失败")
  42. def finish_sim(self):
  43. with self.lock:
  44. vrep.simxStopSimulation(
  45. self.clientID, vrep.simx_opmode_blocking) # 关闭仿真
  46. self.running = False
  47. def get_handle(self, name):
  48. with self.lock:
  49. e, handle = vrep.simxGetObjectHandle(
  50. self.clientID, name, vrep.simx_opmode_oneshot_wait)
  51. if e != 0:
  52. print("[Error]: get {} handle error !!!".format(name))
  53. return handle
  54. def stop_sim(self):
  55. with self.lock:
  56. res = vrep.simxStopSimulation(
  57. self.clientID, vrep.simx_opmode_oneshot)
  58. if res == vrep.simx_return_ok:
  59. print("仿真环境已关闭")
  60. else:
  61. print("[Error]: 仿真环境关闭失败")
  62. def get_pose(self, handle):
  63. """
  64. agv_name: handle
  65. return : agv pose [x, y, z]
  66. """
  67. with self.lock:
  68. e, res = vrep.simxGetObjectPosition(
  69. self.clientID, handle, -1, vrep.simx_opmode_oneshot_wait)
  70. if e != 0:
  71. return [None, None, None]
  72. else:
  73. return res
  74. def get_ori(self, handle):
  75. with self.lock:
  76. e, res = vrep.simxGetObjectOrientation(
  77. self.clientID, handle, -1, vrep.simx_opmode_blocking)
  78. if e != 0:
  79. return [None, None, None]
  80. else:
  81. return np.rad2deg(res)
  82. def get_joint(self, handle):
  83. with self.lock:
  84. e, res = vrep.simxGetJointPosition(
  85. self.clientID, handle, vrep.simx_opmode_oneshot_wait)
  86. if e != 0:
  87. return [None, None, None]
  88. else:
  89. return res
  90. def get_obj_matrix(self, obj_name):
  91. with self.lock:
  92. _, obj_handle = vrep.simxGetObjectHandle(
  93. self.clientID, obj_name, vrep.simx_opmode_oneshot_wait)
  94. return vrep.getObjectMatrix(obj_handle)
  95. def set_joint_velocity(self, handle, speed):
  96. with self.lock:
  97. res = vrep.simxSetJointTargetVelocity(
  98. self.clientID, handle, speed, vrep.simx_opmode_oneshot)

运动控制:

 两轮小车其实是用的一个差速控制,其实就是通过给两个轮子不同的速度让他进行运动,这里就不进行赘述了,具体可以参照

小鱼的两轮差速驱动运动模型 - 知乎

奔驰的战猪 两轮差速机器人运动学模型_两轮差速运动学模型-CSDN博客

python控制接口,通过函数set_joint_velocity进行速度控制,传入两个轮子不同的句柄。

  1. def run(self, agv_num, lspeed, rspeed):
  2. """
  3. speed: 0.029628699166434153 m/s
  4. 或 0.3070062009269009 弧度/s
  5. 或 17.59015959745676 角度/s
  6. 轮子直径: 0.19411166926398632
  7. """
  8. left_motor_handle = self.agvs[agv_num][1]
  9. right_motor_handle = self.agvs[agv_num][2]
  10. r_res = self.set_joint_velocity(right_motor_handle, rspeed)
  11. l_res = self.set_joint_velocity(left_motor_handle, lspeed)
'
运行

Pid部分:

小车在运动的时候受外界因素的影响,需要进行pid控制

  1. class PIDControl:
  2. def __init__(self, Kp=0.25, Ki=0.0, Kd=0.1, integral_upper_limit=20, integral_lower_limit=-20):
  3. self.Kp = Kp #影响相应速度
  4. self.Ki = Ki #补偿
  5. self.Kd = Kd #消除震荡
  6. self.integral_upper_limit = integral_upper_limit
  7. self.integral_lower_limit = integral_lower_limit
  8. self.prev_error = 0
  9. self.integral = 0
  10. def update(self, setpoint, actual_position, dt):
  11. error = setpoint - actual_position
  12. self.integral += error * dt
  13. derivative = self.Kd * (error - self.prev_error) / dt
  14. self.prev_error = error
  15. proportional = self.Kp * error
  16. integral = self.Ki * self.clip(self.integral, self.integral_lower_limit, self.integral_upper_limit)
  17. control_increment = proportional + derivative + integral
  18. print("[PID]: 比例:{:.3f}, 积分:{:.3f}, 微分:{:.3f}, error:{:.3f}, p: {:.3f}"\
  19. .format(proportional,integral, derivative,error,control_increment))
  20. return control_increment
  21. @staticmethod
  22. def clip(value, lower_limit, upper_limit):
  23. if value < lower_limit:
  24. # print(f"lower_limit:{lower_limit}, upper_limit:{upper_limit}")
  25. return lower_limit
  26. elif value > upper_limit:
  27. # print(f"lower_limit:{lower_limit}, upper_limit:{upper_limit}")
  28. return upper_limit
  29. else:
  30. return value
  31. def clear(self):
  32. self.integral = 0
  33. self.prev_error = 0
'
运行

直线运动调用,根据目标的位置和小车的当前角度差进行修正小车方向,

  1. def move_l(self, agv_num, tar_pose, s=5, e=0.1, rate=0.01):
  2. pid = PIDControl(0.05, 0.02, 0.0, 5,-5)
  3. print(f"[Dispath]:agv-{agv_num} runing new potint--target:{tar_pose}")
  4. time_start = time.time()
  5. distance = 100
  6. agv_handle = self.agvs[agv_num][0]
  7. car_pose = self.get_pose(agv_handle)
  8. car_angle = self.get_ori(agv_handle)[2]
  9. car_radians = np.radians(car_angle)
  10. rotation_matrix = np.array([[np.cos(car_radians), -np.sin(car_radians), 0],
  11. [np.sin(car_radians), np.cos(car_radians), 0],
  12. [0, 0, 1]])
  13. vector_ = np.array(
  14. [tar_pose[0] - car_pose[0], tar_pose[1] - car_pose[1], 0])
  15. # 将向量差转换到B坐标系中
  16. transformed_vector = np.dot(rotation_matrix.T, vector_)
  17. dif_angle = np.degrees(np.arctan2(
  18. transformed_vector[1], transformed_vector[0]))
  19. while distance > e:
  20. time.sleep(rate)
  21. car_pose = self.get_pose(agv_handle)
  22. car_angle = self.get_ori(agv_handle)[2]
  23. car_radians = np.radians(car_angle)
  24. rotation_matrix = np.array([[np.cos(car_radians), -np.sin(car_radians), 0],
  25. [np.sin(car_radians), np.cos(car_radians), 0],
  26. [0, 0, 1]])
  27. vector_ = np.array(
  28. [tar_pose[0] - car_pose[0], tar_pose[1] - car_pose[1], 0])
  29. # 将向量差转换到B坐标系中
  30. transformed_vector = np.dot(rotation_matrix.T, vector_)
  31. # 计算旋转角度(方位角度)
  32. dif_angle = np.degrees(np.arctan2(
  33. transformed_vector[1], transformed_vector[0]))
  34. # TODO use pid
  35. distance = np.linalg.norm(vector_)
  36. p = pid.update(0, dif_angle, rate)
  37. print(f"[AGV] movel====>> pid: {p},tar_angle: {0}, dif_angle: {dif_angle}, distanc:{distance} \n")
  38. # 减速
  39. if distance < 0.5:
  40. print(f"[AGV] movel==== 0.5 distance:{distance}")
  41. self.run(agv_num, (s+p)/2, (s-p)/2)
  42. elif distance < 0.1:
  43. print(f"[AGV] movel==== 0.5 distance:{distance}")
  44. self.run(agv_num, (s+p)/3, (s-p)/3)
  45. else:
  46. self.run(agv_num, s+p, s-p)
  47. self.run(agv_num, 0, 0)
  48. # time.sleep(1)
  49. return time.time() - time_start
'
运行

原地旋转,传参为最终目标角,也就是世界坐标系角度。例如tar_angle=180,将车头转向世界坐标系的180。

  1. def move_c(self, agv_num, tar_angle, e=0.25, rate=0.01):
  2. pid = PIDControl(Kp=0.05, Ki=0.0, Kd=0.0005)
  3. time_start = time.time()
  4. cur_angle = self.get_ori(self.agvs[agv_num][0])[2]
  5. tar_matrix = R.from_euler('xyz', [0,0,tar_angle], degrees=True).as_matrix()
  6. while abs(tar_angle - cur_angle) > e:
  7. time.sleep(rate)
  8. cur_angle = self.get_ori(self.agvs[agv_num][0])[2]
  9. cur_rotation_matrix = R.from_euler('xyz', [0,0,cur_angle], degrees=True).as_matrix()
  10. C = np.dot(np.linalg.inv(tar_matrix), cur_rotation_matrix)
  11. yaw = R.from_matrix(C).as_euler('xyz')
  12. yaw = np.degrees(yaw)[-1]
  13. vl = pid.update(0, yaw, rate)
  14. print("[AGV] movec vl:{:.3f}, 误差角:{:.3f}, tar_angle:{:.3f}, cur_angle:{:.3f}\n"\
  15. .format(vl, yaw,tar_angle,cur_angle))
  16. self.run(agv_num, -vl, vl)
  17. self.run(agv_num, 0, 0)
  18. pid.clear()
  19. return time.time() - time_start
'
运行

原地旋转,传参为沿自身偏转角。例如tar_angle=90,将车头逆时针旋转90度。

  1. def move_offset_c(self, agv_num, angle, e=0.5, rate=0.01):
  2. pid = PIDControl(Kp=0.05, Ki=0.0, Kd=0.0005)
  3. time_start = time.time()
  4. cur_angle = self.get_ori(self.agvs[agv_num][0])[2]
  5. cur_rotation_matrix = R.from_euler('xyz', [0,0,cur_angle], degrees=True).as_matrix()
  6. angle_matrix = R.from_euler('xyz', [0,0,angle], degrees=True).as_matrix()
  7. tar_matrix = np.dot(cur_rotation_matrix,angle_matrix)
  8. tar_angle = R.from_matrix(tar_matrix).as_euler('xyz')[-1]
  9. yaw = 10
  10. while abs(yaw) > e:
  11. time.sleep(rate)
  12. cur_angle = self.get_ori(self.agvs[agv_num][0])[2]
  13. cur_rotation_matrix = R.from_euler('xyz', [0,0,cur_angle], degrees=True).as_matrix()
  14. C = np.dot(np.linalg.inv(tar_matrix), cur_rotation_matrix)
  15. yaw = R.from_matrix(C).as_euler('xyz')
  16. yaw = np.degrees(yaw)[-1]
  17. vl = pid.update(0, yaw, rate)
  18. print("[AGV] movec vl:{:.3f}, 误差角:{:.3f}, tar_angle:{:.3f}, cur_angle:{:.3f}\n"\
  19. .format(vl, yaw,tar_angle,cur_angle))
  20. self.run(agv_num, -vl, vl)
  21. self.run(agv_num, 0, 0)
  22. pid.clear()
  23. return time.time() - time_start
'
运行

完整代码:

 完整代码放在https://github.com/lizihan-robot/AGVVrepSim.git

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/937490
推荐阅读
相关标签
  

闽ICP备14008679号