You will certainly want to restrict your angle parameter
vector<lower=-pi(), upper=pi()>[T - 1] turn_angle;
Otherwise the posterior will be highly multi-modal as turn_angle could just be shifted by 2*pi. Furthermore, putting a uniform prior on an unconstrained parameter is asking for trouble.
The disadvantage of restricting the range between -pi and pi is that the sampler cannot easily move across the boundary. Yet, as the parameter actually describes an angle this should be easily possible on a circular space.
Another approach would be to use a unit vector which is then transformed to polar coordinates (shortly discussed in section 19.4 of the Stan manual). I have often found it more effective to use an unconstrained 2-d vector instead with a soft constraint on its length, i.e. len ~ normal(1, 0.1);
The following code illustrates this approach for your model:
functions{
//Function that returns the log pdf of the wrapped-Cauchy
real wrappedCauchy_lpdf(real y, real rho, real mu) {
return(log(1/(2*pi())) + log((1-rho^2)/(1+rho^2-2*rho*cos(y-mu))));
}
}
data {
int<lower=0> T; // length of the time series
vector[T] xo;
vector[T] yo;
}
parameters {
real<lower=0, upper=1> rho;
real<lower=-pi(), upper=pi()> m;
real<lower=0> shape;
real<lower=0> scale;
real<lower=0> sigma;
vector<lower=0>[T - 1] steps;
// unconstrained 2-D vectors
vector[2] angle_raw[T - 1];
}
transformed parameters{
vector[T] x;
vector[T] y;
vector<lower=-pi(), upper=pi()>[T-1] turn_angle;
vector<lower=0>[T-1] angle_len;
// transform to polar coordinates
for (t in 2:T) {
turn_angle[t - 1] = atan2(angle_raw[t - 1, 1], angle_raw[t - 1, 2]);
angle_len[t - 1] = sqrt(angle_raw[t - 1, 1]^2 + angle_raw[t - 1, 2]^2);
}
x[1] = 0;
y[1] = 0;
for(t in 2:T){
x[t] = x[t - 1] + cos(turn_angle[t - 1]) * steps[t - 1];
y[t] = y[t - 1] + sin(turn_angle[t - 1]) * steps[t - 1];
}
}
model {
// soft constraint on vector length
angle_len ~ normal(1, 1e-1);
// Jacobian correction for polar coordinates
target += - sum(log(angle_len));
turn_angle[1] ~ uniform(-pi(), pi());
m ~ normal(0,0.5);
rho ~ beta(4,1);
scale ~ normal(0,1);
shape ~ gamma(2,1);
sigma ~ normal(0, .25);
for(t in 2:T){
steps[t - 1] ~ weibull(shape, scale);
if (t > 2)
turn_angle[t - 1] ~ wrappedCauchy(rho, turn_angle[t - 2] + m);
}
for (t in 1:T) {
xo[t] ~ normal(x[t], sigma);
yo[t] ~ normal(y[t], sigma);
}
}
Note the Jacobian correction to account for the change to polar coordinates. For me this parameterization gets rid of divergent trajectories and low BMI warnings.