simple-obstacle-avoidance.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # ---------------------------------------------------------------------
4 # This example shows how to:
5 # - Call a MVSIM service.
6 # - Use it to drive a vehicle/robot using its internal kinematics
7 # controller.
8 # - Subscribe to 2D lidar data
9 #
10 # Install python3-mvsim, or test with a local build with:
11 # export PYTHONPATH=$HOME/code/mvsim/build/:$PYTHONPATH
12 #
13 # Demo with N robots:
14 # for i in $(seq 1 25); do bash -c "mvsim_tutorial/python/simple-obstacle-avoidance.py --vehicle veh${i} &"; done
15 #
16 # ---------------------------------------------------------------------
17 
18 import argparse
19 import time
20 import math
21 import random
22 from mvsim_comms import pymvsim_comms
23 from mvsim_msgs import SrvSetControllerTwist_pb2
24 from mvsim_msgs import ObservationLidar2D_pb2
25 from mvsim_msgs import SrvGetPose_pb2, SrvGetPoseAnswer_pb2
26 
27 OBS_AVOIDANCE_PERIOD = 0.2 # s
28 V_MAX = 1.0 # m/s
29 VIRTUAL_TARGET_DIST = 2.0 # m
30 NEW_TARGET_PERIOD_SECONDS = 10 # s
31 RANDOM_TARGET_RANGE = 30
32 TARGET_ATTRACTIVE_FORCE = 10
33 
34 parser = argparse.ArgumentParser(prog='simple-obstacle-avoidance')
35 parser.add_argument('--vehicle', dest='vehicleName', action='store', required=True,
36  help='The name of the vehicle to control in MVSIM')
37 
38 args = parser.parse_args()
39 
40 client = pymvsim_comms.mvsim.Client()
41 random.seed()
42 
43 global prevLidarMsgTimestamp
44 prevLidarMsgTimestamp = 0
45 
46 global prevGlobalGoalTimestamp
47 global prevGlobalGoal
48 prevGlobalGoal = [0, 0]
49 prevGlobalGoalTimestamp = 0
50 
51 
52 def sendRobotTwistSetpoint(client, robotName, vx, vy, w):
53  # (vx,vy) in local coordinates [m/s]
54  # (w) in [rad/s]
55 
56  req = SrvSetControllerTwist_pb2.SrvSetControllerTwist()
57  req.objectId = robotName # vehicle/robot/object name in MVSIM
58  req.twistSetPoint.vx = vx
59  req.twistSetPoint.vy = vy
60  req.twistSetPoint.vz = 0
61  req.twistSetPoint.wx = 0
62  req.twistSetPoint.wy = 0
63  req.twistSetPoint.wz = w
64  # ret =
65  client.callService('set_controller_twist', req.SerializeToString())
66 
67 
68 def getRobotPose(client, robotName):
69  req = SrvGetPose_pb2.SrvGetPose()
70  req.objectId = robotName # vehicle/robot/object name in MVSIM
71  ret = client.callService('get_pose', req.SerializeToString())
72  ans = SrvGetPoseAnswer_pb2.SrvGetPoseAnswer()
73  ans.ParseFromString(ret)
74  print(ans)
75 
76 
77 def evalObstacleAvoidance(obs: ObservationLidar2D_pb2.ObservationLidar2D):
78  ang = -obs.aperture*0.5
79  dA = obs.aperture / (len(obs.scanRanges)-1)
80 
81  # Force vectors
82  resultantForce = [0, 0]
83 
84  #obsWeight = 20.0 / len(obs.scanRanges)
85  obsWeight = 1
86 
87  for idx, r in enumerate(obs.scanRanges):
88  ang += dA
89  # obs.maxRange
90  # obs.scanRanges
91  # obs.sensorPose
92  #
93  # Obstacles:
94  # Compute force strength:
95  mod = min(1e3, 1.0 / (r*r))
96 
97  # Add repulsive force:
98  fx = -math.cos(ang) * mod
99  fy = -math.sin(ang) * mod
100  resultantForce[0] += fx * obsWeight
101  resultantForce[1] += fy * obsWeight
102 
103  # Target:
104  # robotPose = getRobotPose(client, args.vehicleName)
105  # TODO: Compute relative vector
106  ang = 0
107  resultantForce[0] += math.cos(ang) * TARGET_ATTRACTIVE_FORCE
108  resultantForce[1] += math.sin(ang) * TARGET_ATTRACTIVE_FORCE
109 
110  # Result:
111  desiredDirection = 0
112  if (resultantForce[0] != 0) or (resultantForce[1] != 0):
113  desiredDirection = math.atan2(resultantForce[1], resultantForce[0])
114 
115  # Convert direction to differential-driven command:
116  # Build a "virtual target point" and calculate the (v,w) to drive there.
117  target_x = VIRTUAL_TARGET_DIST*math.cos(desiredDirection)
118  target_y = VIRTUAL_TARGET_DIST*math.sin(desiredDirection)
119 
120  if (target_x < 0):
121  v = -V_MAX
122  else:
123  v = V_MAX
124 
125  if (target_y == 0):
126  w = 0
127  else:
128  R = math.sqrt((target_x*target_x + target_y *
129  target_y)/(2*abs(target_y)))
130  if (target_y < 0):
131  R = -R
132  w = v / R
133 
134  return [v, w]
135 
136 
137 def onLidar2DMessage(msgType, msg):
138  global prevLidarMsgTimestamp
139  global prevGlobalGoalTimestamp, prevGlobalGoal
140 
141  assert(msgType == "mvsim_msgs.ObservationLidar2D")
142  p = ObservationLidar2D_pb2.ObservationLidar2D()
143  p.ParseFromString(bytes(msg))
144  # print("[lidar callback] received:\n ranges=\n" + str(p.scanRanges) + "\n validRanges=" + str(p.validRanges))
145 
146  if (p.unixTimestamp > prevLidarMsgTimestamp+OBS_AVOIDANCE_PERIOD):
147  prevLidarMsgTimestamp = p.unixTimestamp
148  [v, w] = evalObstacleAvoidance(p)
149  sendRobotTwistSetpoint(client, args.vehicleName, v, 0, w)
150 
151  if (p.unixTimestamp > prevGlobalGoalTimestamp+NEW_TARGET_PERIOD_SECONDS):
152  prevGlobalGoalTimestamp = p.unixTimestamp
153  prevGlobalGoal = [(-0.5+random.random())*RANDOM_TARGET_RANGE,
154  (-0.5+random.random())*RANDOM_TARGET_RANGE]
155 
156 
157 if __name__ == "__main__":
158  client.setName("obstacle-avoidance")
159  print("Connecting to server...")
160  client.connect()
161  print("Connected successfully.")
162 
163  print("===========================")
164  print("Configuration ")
165  print("- Vehicle to control: " + args.vehicleName)
166  print("===========================")
167 
168  # Subscribe to "/r1/laser1_scan"
169  client.subscribeTopic("/" + args.vehicleName +
170  "/laser1_scan", onLidar2DMessage)
171 
172  time.sleep(60)
173  sendRobotTwistSetpoint(client, args.vehicleName, 0, 0, 0)
OutIt print(OutIt out, const xml_node< Ch > &node, int flags=0)
static int min(int n1, int n2)
Definition: wl_init.c:42
def getRobotPose(client, robotName)
def sendRobotTwistSetpoint(client, robotName, vx, vy, w)


mvsim
Author(s):
autogenerated on Tue Jul 4 2023 03:08:21