solarcarsim/notebooks/v1sim.ipynb

216 lines
108 KiB
Plaintext
Raw Normal View History

2024-12-12 01:59:37 +00:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Version 1 Simulation\n",
"\n",
"The first version of this series is a basic control model. Given an elevation profile $H(x)$ and a time target, minimize energy usage.\n",
"We assume the time target is constant, since we are racing at a given overall pace. In other words, we already know the average speed $E(V) = dist/time$"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import jax.numpy as jnp\n",
"from jax import jit, vmap, lax\n",
"from jax import random\n",
"import matplotlib.pyplot as plt\n",
"\n",
"@jit\n",
"def _cov_math(t, s, H):\n",
" return 0.5 * (jnp.abs(t) ** 2 * H + jnp.abs(s) ** 2 * H - jnp.abs(t - s) ** 2 * H)\n",
"\n",
"\n",
"def _fbm_covariance(n, H) -> jnp.ndarray:\n",
" tidx = jnp.arange(1, n + 1)\n",
" t, s = jnp.meshgrid(tidx, tidx)\n",
"\n",
" # fBm covariance equation from wikipedia\n",
" cov = 0.5 * (jnp.abs(t) ** 2 * H + jnp.abs(s) ** 2 * H - jnp.abs(t - s) ** 2 * H)\n",
" return cov\n",
"\n",
"# generate terrain using fractional brownian motion\n",
"def gen_elevation_profile(rngkey: random.PRNGKey, n_steps: int, H: float):\n",
" t = jnp.linspace(0,1,n_steps)\n",
" cov = _fbm_covariance(n_steps, H)\n",
" # using the \"method 1\" (cholesky decomposition)\n",
" sigma = jnp.linalg.cholesky(cov)\n",
" # create a vector of n_steps gaussian normal values\n",
" v = random.normal(rngkey, shape=(n_steps))\n",
" # convert these to fbm lines\n",
"\n",
" fbm_samples = sigma * v\n",
"\n",
" return t, fbm_samples\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[nan -0. -0. ... -0. -0. -0.]\n",
" [nan nan -0. ... -0. -0. -0.]\n",
" [nan nan nan ... -0. -0. -0.]\n",
" ...\n",
" [nan nan nan ... nan -0. -0.]\n",
" [nan nan nan ... nan nan -0.]\n",
" [nan nan nan ... nan nan nan]]\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1200x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"key = random.PRNGKey(0)\n",
"steps = 1000\n",
"samples = 5\n",
"\n",
"H = 0.6\n",
"\n",
"t, fbm = gen_elevation_profile(key, steps, H)\n",
"plt.figure(figsize=(12,6))\n",
"print(fbm)\n",
"for i in range(fbm.shape[0]):\n",
" plt.plot(t, fbm[i], label=f\"Sample {i}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7a576474c680>]"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"steps = 100\n",
"key = random.key(25)\n",
"\n",
"def uniform_window(n):\n",
" return jnp.ones(n)/n\n",
"\n",
"def generate_basic_terrain(key, steps=100, yscale=1.0, xscale=10.0, window=uniform_window, window_size=5):\n",
" key, split = random.split(key)\n",
" v = random.normal(split, shape=(steps))\n",
" y = jnp.cumsum(v) * yscale\n",
" # smooth with a windowing function\n",
" y_smooth = jnp.convolve(y, window(window_size), mode='same')\n",
" # compute the x-values\n",
" x = jnp.arange(steps) * xscale\n",
" return x,y_smooth\n",
"\n",
"\n",
"x,y = generate_basic_terrain(key)\n",
" \n",
"slope = jnp.atan(jnp.diff(y, prepend=0) / 10.0) * 180 / jnp.pi\n",
"plt.plot(x,y)\n",
"plt.plot(x, slope)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x7a57742cd0d0>]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# we can compute the slope at any point along the terrain\n",
"plt.plot(x, slope)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.7"
}
},
"nbformat": 4,
"nbformat_minor": 2
}