illuminato
illuminato

Reputation: 1257

Python: How to create trajectory between start and end positions with specified speeds in a vectorized way?

I am trying to find a way to create a trajectory between two points with specified speeds in a vectorized way. For example start point is [0, 0] and end point is [25, 15]. Next I need to specified speeds [25, 4, 8, 2] and their corresponding probabilities [0.4, 0.2, 0.3, 0.1]. So for each time interval, speed can be 25 m/s (with probability 40%), or 4 m/s (probability 20%), etc.

Here is an example of desired output:

[[0,0], [3,1], [6,3.5], [12,7], [14,8], [19,11], [25,15]]

As you see object moved from [0,0] to [3,1] with speed e.g. 4 m/s, next from [3,1] to [6,3.5] with speed e.g. 8 m/s, etc.

(note it is just example with approximate coordinates)

Here is my attempt to create a script to generate such trajectories:

from math import asin, degrees

ue_speed = [3, 4, 8, 25]
ue_speed_prob = [0.4, 0.2, 0.3, 0.1]
steps = 20 # this parameter hardcoded and should be removed
square_size = 100 # need for scaling
time_interval = 100 # need for scaling
start_coord = [0, 0]
end_coord = [25, 15]
b = end_coord[0] - start_coord[0]
a = end_coord[1] - start_coord[1]
c = (a**2 + b**2)**0.5
theta =  [degrees(asin(a/c))] * (steps - 1)
start = [start_coord]

v = np.random.choice(ue_speed, size=steps-1, p=ue_speed_prob)
R = np.expand_dims(((v * time_interval) / square_size), axis=-1)
xy = np.dstack((np.cos(theta), np.sin(theta))) * R
trajectory_ = np.hstack((np.zeros((1, 1, 2)), np.cumsum(xy, axis=1)))
trajectory = np.abs(trajectory_[0] + start )

Output:

array([[ 0.        ,  0.        ],
       [ 2.69850332,  1.31075545],
       [ 5.39700664,  2.62151089],
       [ 8.09550996,  3.93226634],
       [10.79401327,  5.24302179],
       [14.3920177 ,  6.99069571],
       [21.58802655, 10.48604357],
       [24.28652987, 11.79679902],
       [26.98503318, 13.10755446],
       [30.58303761, 14.85522839],
       [33.28154093, 16.16598384],
       [35.98004425, 17.47673929],
       [38.67854756, 18.78749473],
       [45.87455641, 22.28284259],
       [49.47256084, 24.03051652],
       [56.66856969, 27.52586437],
       [59.36707301, 28.83661982],
       [62.06557632, 30.14737527],
       [65.66358075, 31.8950492 ],
       [69.26158517, 33.64272312]])

Output is not correct. End point should be [25, 15].

Is it possible to change somehow code above to generate correct results?

Upvotes: 1

Views: 2781

Answers (2)

Jakob Stark
Jakob Stark

Reputation: 3845

First I think you were fooled by a simple but yet subtle mathematical error you made. In your illustration, you show the first path segment to be from (0,0) to (3,1). This however does not correspond to a "speed" of 4, as your object moves along the diagonal path. For the first line segment you would get a speed of

v = (1**2 + 3**2)**0.5 (= 3.162)

Even less obvious to me is, how you got to a speed of 4 in the second line segment, but you mentioned approximate coordinates, so I will assume, that these are not the exact coordinates that you are looking for.

That put aside, you will not generally be able to get to the exact ending point with different specified speeds, so I will show a solution, that gets just past the ending point and stops there.

import numpy as np

ue_speed = [3, 4, 8, 25]
ue_speed_prob = [0.4, 0.2, 0.3, 0.1]
square_size = 100 # need for scaling
time_interval = 100 # need for scaling
start_coord = [0, 0]
end_coord = [25, 15]
b = end_coord[0] - start_coord[0]
a = end_coord[1] - start_coord[1]
c = (a**2+b**2)**0.5

theta =  np.arctan2(a,b)

steps = int(c//(min(ue_speed)*time_interval/square_size) + 1) # fixed scaling issue
v = np.random.choice(ue_speed, size=steps, p=ue_speed_prob)
R = np.cumsum((v * time_interval) / square_size)
R = R[:np.argmax(R>c)+1]
P = np.column_stack((R*np.cos(theta)+start_coord[0],
                     R*np.sin(theta)+start_coord[1]))
trajectory = P

This will print trajectory as

[[ 2.57247878  1.54348727]
 [ 5.14495755  3.08697453]
 [12.00490096  7.20294058]
 [33.4422241  20.06533446]]

Note: You also made a very serious error in your code, when your converted the result of the math.asin to degrees and then used it again in trigonometric functions like np.sin and np.cos. I strongly recommand you to stick to radiant angle values and only convert them to degrees if you want to print them out.

Also I recommend the use of an arctan2 like function, to correctly get the angle of an x and y coordinate, as this will also work for negative x directions.

Upvotes: 1

Gulzar
Gulzar

Reputation: 27966

Correct, vectorized solution follows.

You will notice one of the samples is the end point. You can play with the numbers.

Also, I think it is best staying with Cartesian coordinates. No need to work with angles at all for this.

import numpy as np

# ue_speed = np.array([3, 4, 8, 25])
ue_speed = np.array([1, 1, 1, 1])
ue_speed_prob = np.array([0.4, 0.2, 0.3, 0.1])
steps = 200  # this parameter hardcoded and should be removed
time_interval = 1  # need for scaling
start_coord = np.array([0, 0])
end_coord = np.array([25, 15])

r_direction = end_coord - start_coord
r_hat = r_direction / np.linalg.norm(r_direction)

v = np.random.choice(ue_speed, size=steps + 1, p=ue_speed_prob)
dr = v * time_interval

dr_vecs = r_hat[np.newaxis, :] * dr[:, np.newaxis]
r_vecs = np.cumsum(dr_vecs, axis=0)
r = start_coord + r_vecs

print(r_vecs)

With given parameters, this outputs

[[  0.85749293   0.51449576]
 [  1.71498585   1.02899151]
 [  2.57247878   1.54348727]
 [  3.4299717    2.05798302]
 [  4.28746463   2.57247878]
 [  5.14495755   3.08697453]
 [  6.00245048   3.60147029]
 [  6.85994341   4.11596604]
 [  7.71743633   4.6304618 ]
 [  8.57492926   5.14495755]
 [  9.43242218   5.65945331]
 [ 10.28991511   6.17394907]
 [ 11.14740803   6.68844482]
 [ 12.00490096   7.20294058]
 [ 12.86239389   7.71743633]
 [ 13.71988681   8.23193209]
 [ 14.57737974   8.74642784]
 [ 15.43487266   9.2609236 ]
 [ 16.29236559   9.77541935]
 [ 17.14985851  10.28991511]
 [ 18.00735144  10.80441086]
 [ 18.86484437  11.31890662]
 [ 19.72233729  11.83340237]
 [ 20.57983022  12.34789813]
 [ 21.43732314  12.86239389]
 [ 22.29481607  13.37688964]
 [ 23.15230899  13.8913854 ]
 [ 24.00980192  14.40588115]
 [ 24.86729485  14.92037691]
 [ 25.72478777  15.43487266]
 [ 26.5822807   15.94936842]
 [ 27.43977362  16.46386417]
 [ 28.29726655  16.97835993]
 [ 29.15475947  17.49285568]
 [ 30.0122524   18.00735144]
 [ 30.86974533  18.5218472 ]
 [ 31.72723825  19.03634295]
 [ 32.58473118  19.55083871]
 [ 33.4422241   20.06533446]
 [ 34.29971703  20.57983022]
 [ 35.15720995  21.09432597]
 [ 36.01470288  21.60882173]
 [ 36.87219581  22.12331748]
 [ 37.72968873  22.63781324]
 [ 38.58718166  23.15230899]
 [ 39.44467458  23.66680475]
 [ 40.30216751  24.18130051]
 [ 41.15966043  24.69579626]
 [ 42.01715336  25.21029202]
 [ 42.87464629  25.72478777]
 [ 43.73213921  26.23928353]
 [ 44.58963214  26.75377928]
 [ 45.44712506  27.26827504]
 [ 46.30461799  27.78277079]
 [ 47.16211091  28.29726655]
 [ 48.01960384  28.8117623 ]
 [ 48.87709677  29.32625806]
 [ 49.73458969  29.84075381]
 [ 50.59208262  30.35524957]
 [ 51.44957554  30.86974533]
 [ 52.30706847  31.38424108]
 [ 53.16456139  31.89873684]
 [ 54.02205432  32.41323259]
 [ 54.87954725  32.92772835]
 [ 55.73704017  33.4422241 ]
 [ 56.5945331   33.95671986]
 [ 57.45202602  34.47121561]
 [ 58.30951895  34.98571137]
 [ 59.16701187  35.50020712]
 [ 60.0245048   36.01470288]
 [ 60.88199773  36.52919864]
 [ 61.73949065  37.04369439]
 [ 62.59698358  37.55819015]
 [ 63.4544765   38.0726859 ]
 [ 64.31196943  38.58718166]
 [ 65.16946235  39.10167741]
 [ 66.02695528  39.61617317]
 [ 66.88444821  40.13066892]
 [ 67.74194113  40.64516468]
 [ 68.59943406  41.15966043]
 [ 69.45692698  41.67415619]
 [ 70.31441991  42.18865195]
 [ 71.17191283  42.7031477 ]
 [ 72.02940576  43.21764346]
 [ 72.88689869  43.73213921]
 [ 73.74439161  44.24663497]
 [ 74.60188454  44.76113072]
 [ 75.45937746  45.27562648]
 [ 76.31687039  45.79012223]
 [ 77.17436331  46.30461799]
 [ 78.03185624  46.81911374]
 [ 78.88934917  47.3336095 ]
 [ 79.74684209  47.84810525]
 [ 80.60433502  48.36260101]
 [ 81.46182794  48.87709677]
 [ 82.31932087  49.39159252]
 [ 83.17681379  49.90608828]
 [ 84.03430672  50.42058403]
 [ 84.89179965  50.93507979]
 [ 85.74929257  51.44957554]
 [ 86.6067855   51.9640713 ]
 [ 87.46427842  52.47856705]
 [ 88.32177135  52.99306281]
 [ 89.17926427  53.50755856]
 [ 90.0367572   54.02205432]
 [ 90.89425013  54.53655008]
 [ 91.75174305  55.05104583]
 [ 92.60923598  55.56554159]
 [ 93.4667289   56.08003734]
 [ 94.32422183  56.5945331 ]
 [ 95.18171475  57.10902885]
 [ 96.03920768  57.62352461]
 [ 96.89670061  58.13802036]
 [ 97.75419353  58.65251612]
 [ 98.61168646  59.16701187]
 [ 99.46917938  59.68150763]
 [100.32667231  60.19600339]
 [101.18416523  60.71049914]
 [102.04165816  61.2249949 ]
 [102.89915109  61.73949065]
 [103.75664401  62.25398641]
 [104.61413694  62.76848216]
 [105.47162986  63.28297792]
 [106.32912279  63.79747367]
 [107.18661571  64.31196943]
 [108.04410864  64.82646518]
 [108.90160157  65.34096094]
 [109.75909449  65.85545669]
 [110.61658742  66.36995245]
 [111.47408034  66.88444821]
 [112.33157327  67.39894396]
 [113.18906619  67.91343972]
 [114.04655912  68.42793547]
 [114.90405205  68.94243123]
 [115.76154497  69.45692698]
 [116.6190379   69.97142274]
 [117.47653082  70.48591849]
 [118.33402375  71.00041425]
 [119.19151667  71.51491   ]
 [120.0490096   72.02940576]
 [120.90650253  72.54390152]
 [121.76399545  73.05839727]
 [122.62148838  73.57289303]
 [123.4789813   74.08738878]
 [124.33647423  74.60188454]
 [125.19396715  75.11638029]
 [126.05146008  75.63087605]
 [126.90895301  76.1453718 ]
 [127.76644593  76.65986756]
 [128.62393886  77.17436331]
 [129.48143178  77.68885907]
 [130.33892471  78.20335482]
 [131.19641763  78.71785058]
 [132.05391056  79.23234634]
 [132.91140349  79.74684209]
 [133.76889641  80.26133785]
 [134.62638934  80.7758336 ]
 [135.48388226  81.29032936]
 [136.34137519  81.80482511]
 [137.19886811  82.31932087]
 [138.05636104  82.83381662]
 [138.91385397  83.34831238]
 [139.77134689  83.86280813]
 [140.62883982  84.37730389]
 [141.48633274  84.89179965]
 [142.34382567  85.4062954 ]
 [143.20131859  85.92079116]
 [144.05881152  86.43528691]
 [144.91630445  86.94978267]
 [145.77379737  87.46427842]
 [146.6312903   87.97877418]
 [147.48878322  88.49326993]
 [148.34627615  89.00776569]
 [149.20376907  89.52226144]
 [150.061262    90.0367572 ]
 [150.91875493  90.55125296]
 [151.77624785  91.06574871]
 [152.63374078  91.58024447]
 [153.4912337   92.09474022]
 [154.34872663  92.60923598]
 [155.20621955  93.12373173]
 [156.06371248  93.63822749]
 [156.92120541  94.15272324]
 [157.77869833  94.667219  ]
 [158.63619126  95.18171475]
 [159.49368418  95.69621051]
 [160.35117711  96.21070626]
 [161.20867003  96.72520202]
 [162.06616296  97.23969778]
 [162.92365589  97.75419353]
 [163.78114881  98.26868929]
 [164.63864174  98.78318504]
 [165.49613466  99.2976808 ]
 [166.35362759  99.81217655]
 [167.21112051 100.32667231]
 [168.06861344 100.84116806]
 [168.92610637 101.35566382]
 [169.78359929 101.87015957]
 [170.64109222 102.38465533]
 [171.49858514 102.89915109]
 [172.35607807 103.41364684]]



BONUS

if you want to not hard code the steps, you can do

steps = int((np.linalg.norm(end_coord - start_coord) / np.min(ue_speed)) / time_interval)  
steps = steps if steps > 0 else 1

and the last sample will be approximately the end coord.

Upvotes: 0

Related Questions