Initial commit
This commit is contained in:
200
docs/tutorials/robotics/camera-frustums.md
Normal file
200
docs/tutorials/robotics/camera-frustums.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# Camera Frustums in Robotics Visualization
|
||||
|
||||
## Overview
|
||||
|
||||
Camera frustums are essential for visualizing camera viewpoints in robotics applications. Vuer allows you to programmatically insert camera frustums into the scene to represent camera positions and orientations.
|
||||
|
||||
## Basic Frustum
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Scene, Frustum, DefaultScene
|
||||
import asyncio
|
||||
|
||||
app = Vuer()
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session):
|
||||
session.set @ Scene(
|
||||
DefaultScene(),
|
||||
|
||||
Frustum(
|
||||
position=[0, 1, 2],
|
||||
rotation=[0, 0, 0],
|
||||
scale=[1, 1, 1],
|
||||
showImagePlane=True,
|
||||
showFrustum=True,
|
||||
showFocalPlane=True,
|
||||
key="camera-frustum",
|
||||
),
|
||||
)
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Frustum Configuration Options
|
||||
|
||||
### showImagePlane
|
||||
Display the image plane (where the image is captured).
|
||||
|
||||
### showFrustum
|
||||
Show the frustum wireframe (pyramid shape representing the camera's field of view).
|
||||
|
||||
### showFocalPlane
|
||||
Display the focal plane (plane at the focal length).
|
||||
|
||||
### Position and Orientation
|
||||
- **position**: `[x, y, z]` coordinates
|
||||
- **rotation**: Euler angles `[x, y, z]` in radians
|
||||
- **scale**: `[x, y, z]` scale factors
|
||||
|
||||
## Stress Test Example: 1,728 Frustums
|
||||
|
||||
The tutorial demonstrates a stress-test implementation with a large grid of frustums:
|
||||
|
||||
```python
|
||||
from vuer import Vuer, VuerSession
|
||||
from vuer.schemas import Scene, Frustum, DefaultScene
|
||||
import asyncio
|
||||
|
||||
app = Vuer()
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session: VuerSession):
|
||||
frustums = []
|
||||
|
||||
# Create 12×12×12 grid of frustums
|
||||
for x in range(12):
|
||||
for y in range(12):
|
||||
for z in range(12):
|
||||
frustums.append(
|
||||
Frustum(
|
||||
position=[x * 2, y * 2, z * 2],
|
||||
scale=[0.5, 0.5, 0.5],
|
||||
showImagePlane=True,
|
||||
showFrustum=True,
|
||||
showFocalPlane=False,
|
||||
key=f"frustum-{x}-{y}-{z}",
|
||||
)
|
||||
)
|
||||
|
||||
session.set @ Scene(
|
||||
DefaultScene(),
|
||||
*frustums, # Unpack all frustums into the scene
|
||||
)
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
This creates **1,728 frustum objects** (12³), demonstrating the framework's capability to handle large numbers of camera visualization objects simultaneously.
|
||||
|
||||
## Practical Use Case: Multi-Camera Robot
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Scene, Frustum, Urdf, DefaultScene
|
||||
import asyncio
|
||||
|
||||
app = Vuer()
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session):
|
||||
session.set @ Scene(
|
||||
DefaultScene(),
|
||||
|
||||
# Robot model
|
||||
Urdf(
|
||||
src="/static/robot.urdf",
|
||||
position=[0, 0, 0],
|
||||
key="robot",
|
||||
),
|
||||
|
||||
# Front camera
|
||||
Frustum(
|
||||
position=[0.5, 0.5, 0],
|
||||
rotation=[0, 0, 0],
|
||||
scale=[0.3, 0.3, 0.3],
|
||||
showImagePlane=True,
|
||||
showFrustum=True,
|
||||
key="front-camera",
|
||||
),
|
||||
|
||||
# Left camera
|
||||
Frustum(
|
||||
position=[0, 0.5, 0.5],
|
||||
rotation=[0, -1.57, 0],
|
||||
scale=[0.3, 0.3, 0.3],
|
||||
showImagePlane=True,
|
||||
showFrustum=True,
|
||||
key="left-camera",
|
||||
),
|
||||
|
||||
# Right camera
|
||||
Frustum(
|
||||
position=[0, 0.5, -0.5],
|
||||
rotation=[0, 1.57, 0],
|
||||
scale=[0.3, 0.3, 0.3],
|
||||
showImagePlane=True,
|
||||
showFrustum=True,
|
||||
key="right-camera",
|
||||
),
|
||||
)
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Dynamic Frustum Updates
|
||||
|
||||
You can update frustum positions in real-time:
|
||||
|
||||
```python
|
||||
import math
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session):
|
||||
session.set @ Scene(DefaultScene())
|
||||
|
||||
for i in range(1000):
|
||||
# Orbit the frustum around the origin
|
||||
x = 3 * math.cos(i * 0.05)
|
||||
z = 3 * math.sin(i * 0.05)
|
||||
rotation_y = i * 0.05
|
||||
|
||||
session.upsert @ Frustum(
|
||||
position=[x, 1, z],
|
||||
rotation=[0, rotation_y, 0],
|
||||
showImagePlane=True,
|
||||
showFrustum=True,
|
||||
key="orbiting-camera",
|
||||
)
|
||||
|
||||
await asyncio.sleep(0.033) # ~30 FPS
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
The stress test with 1,728 frustums demonstrates that Vuer can handle:
|
||||
- Large numbers of visualization objects
|
||||
- Complex geometric primitives
|
||||
- Real-time rendering of camera representations
|
||||
|
||||
This makes it practical for robotics applications requiring:
|
||||
- Multi-camera system visualization
|
||||
- SLAM trajectory visualization
|
||||
- Sensor fusion displays
|
||||
- Camera calibration tools
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/robotics/frustums.html
|
||||
181
docs/tutorials/robotics/go1-stairs.md
Normal file
181
docs/tutorials/robotics/go1-stairs.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Unitree Go1 Robot with Stairs
|
||||
|
||||
## Overview
|
||||
This tutorial demonstrates setting up a 3D scene containing a Unitree Go1 quadruped robot positioned in front of a staircase, along with lighting and atmospheric effects.
|
||||
|
||||
## Scene Components
|
||||
|
||||
The visualization includes four main elements:
|
||||
|
||||
1. **Unitree Go1 Robot** - A quadrupedal robot model loaded via URDF format
|
||||
2. **Stairway Mesh** - A textured 3D model of stairs
|
||||
3. **Fog Effect** - Atmospheric fog that darkens distant portions of the scene
|
||||
4. **Lighting** - An ambient light source plus two movable point lights
|
||||
|
||||
## Complete Code Example
|
||||
|
||||
```python
|
||||
import math
|
||||
import asyncio
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Scene, Urdf, Obj, AmbientLight, PointLight, Movable, Plane, Fog
|
||||
|
||||
app = Vuer()
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session):
|
||||
# Setup scene with fog and lighting
|
||||
session.set @ Scene(
|
||||
# Add fog effect
|
||||
Fog(
|
||||
color="#000000",
|
||||
near=1,
|
||||
far=20,
|
||||
),
|
||||
|
||||
# Ground plane
|
||||
Plane(
|
||||
args=[100, 100],
|
||||
position=[0, -0.01, 0],
|
||||
rotation=[-1.57, 0, 0],
|
||||
key="ground",
|
||||
),
|
||||
|
||||
# Staircase mesh
|
||||
Obj(
|
||||
src="/static/stairs/stairs.obj",
|
||||
position=[2, 0, 0],
|
||||
rotation=[0, 0, 0],
|
||||
materialType="standard",
|
||||
material={
|
||||
"color": "#cccccc",
|
||||
"roughness": 0.8,
|
||||
},
|
||||
key="stairs",
|
||||
),
|
||||
|
||||
# Ambient lighting
|
||||
AmbientLight(intensity=1.0),
|
||||
|
||||
# Movable point lights
|
||||
Movable(
|
||||
PointLight(
|
||||
intensity=3.0,
|
||||
position=[2, 3, 2],
|
||||
key="light-1",
|
||||
)
|
||||
),
|
||||
Movable(
|
||||
PointLight(
|
||||
intensity=3.0,
|
||||
position=[-2, 3, -2],
|
||||
key="light-2",
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
# Animation loop for the robot
|
||||
for i in range(10000):
|
||||
# Calculate joint angles using sinusoidal functions
|
||||
hip_angle = 0.3 * math.sin(i * 0.1)
|
||||
thigh_angle = 0.785 - 0.25 * math.sin(i * 0.1)
|
||||
calf_angle = -1.5 + 0.5 * math.sin(i * 0.1)
|
||||
|
||||
# Update robot position and joints
|
||||
session.upsert @ Urdf(
|
||||
src="/static/go1/go1.urdf",
|
||||
position=[0, 0, 0.33],
|
||||
rotation=[0, 0, 0],
|
||||
jointValues={
|
||||
# Front Left
|
||||
"FL_hip_joint": hip_angle,
|
||||
"FL_thigh_joint": thigh_angle,
|
||||
"FL_calf_joint": calf_angle,
|
||||
|
||||
# Front Right
|
||||
"FR_hip_joint": -hip_angle,
|
||||
"FR_thigh_joint": thigh_angle,
|
||||
"FR_calf_joint": calf_angle,
|
||||
|
||||
# Rear Left
|
||||
"RL_hip_joint": -hip_angle,
|
||||
"RL_thigh_joint": thigh_angle,
|
||||
"RL_calf_joint": calf_angle,
|
||||
|
||||
# Rear Right
|
||||
"RR_hip_joint": hip_angle,
|
||||
"RR_thigh_joint": thigh_angle,
|
||||
"RR_calf_joint": calf_angle,
|
||||
},
|
||||
key="go1-robot",
|
||||
)
|
||||
|
||||
# Update at ~60 FPS
|
||||
await asyncio.sleep(0.016)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
### Fog Effect
|
||||
Creates atmospheric depth:
|
||||
```python
|
||||
Fog(
|
||||
color="#000000", # Black fog
|
||||
near=1, # Fog starts at distance 1
|
||||
far=20, # Full fog at distance 20
|
||||
)
|
||||
```
|
||||
|
||||
### Ground Plane
|
||||
A large plane rotated to be horizontal:
|
||||
```python
|
||||
Plane(
|
||||
args=[100, 100], # 100x100 units
|
||||
position=[0, -0.01, 0], # Slightly below origin
|
||||
rotation=[-1.57, 0, 0], # Rotated 90° (π/2)
|
||||
)
|
||||
```
|
||||
|
||||
### Staircase Mesh
|
||||
3D model with material properties:
|
||||
```python
|
||||
Obj(
|
||||
src="/static/stairs/stairs.obj",
|
||||
materialType="standard",
|
||||
material={
|
||||
"color": "#cccccc",
|
||||
"roughness": 0.8,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## Robot Animation
|
||||
|
||||
The application updates continuously at approximately 60 frames per second, calculating joint values like:
|
||||
|
||||
```python
|
||||
thigh_angle = 0.785 - 0.25 * math.sin(i * 0.1)
|
||||
```
|
||||
|
||||
This creates realistic walking motion across the stair scene.
|
||||
|
||||
## Leg Coordination
|
||||
|
||||
The Go1 has a specific gait pattern:
|
||||
- Front Left and Rear Right move together (same hip angle)
|
||||
- Front Right and Rear Left move together (opposite hip angle)
|
||||
|
||||
This creates a natural trotting gait.
|
||||
|
||||
## Assets Required
|
||||
|
||||
Make sure you have the following assets:
|
||||
- `go1.urdf` - Robot description file
|
||||
- `stairs.obj` - Staircase 3D model
|
||||
- Associated mesh files for the Go1 robot
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/robotics/urdf_go1_stairs.html
|
||||
148
docs/tutorials/robotics/mini-cheetah.md
Normal file
148
docs/tutorials/robotics/mini-cheetah.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# MIT Mini Cheetah URDF Tutorial
|
||||
|
||||
## Overview
|
||||
This tutorial demonstrates serving a URDF (Unified Robot Description Format) file locally to visualize the MIT Mini Cheetah robot in Vuer with animated leg movements.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
Set up your assets directory:
|
||||
|
||||
```
|
||||
assets/mini_cheetah/
|
||||
├── meshes/
|
||||
│ ├── mini_abad.obj
|
||||
│ ├── mini_body.obj
|
||||
│ ├── mini_lower_link.obj
|
||||
│ └── mini_upper_link.obj
|
||||
└── mini_cheetah.urdf
|
||||
```
|
||||
|
||||
## Download Assets
|
||||
|
||||
Use wget to fetch the URDF and mesh files:
|
||||
|
||||
```bash
|
||||
mkdir -p assets/mini_cheetah/meshes
|
||||
cd assets/mini_cheetah
|
||||
|
||||
# Download URDF file
|
||||
wget https://raw.githubusercontent.com/vuer-ai/vuer/main/assets/mini_cheetah/mini_cheetah.urdf
|
||||
|
||||
# Download mesh files
|
||||
cd meshes
|
||||
wget https://raw.githubusercontent.com/vuer-ai/vuer/main/assets/mini_cheetah/meshes/mini_abad.obj
|
||||
wget https://raw.githubusercontent.com/vuer-ai/vuer/main/assets/mini_cheetah/meshes/mini_body.obj
|
||||
wget https://raw.githubusercontent.com/vuer-ai/vuer/main/assets/mini_cheetah/meshes/mini_lower_link.obj
|
||||
wget https://raw.githubusercontent.com/vuer-ai/vuer/main/assets/mini_cheetah/meshes/mini_upper_link.obj
|
||||
```
|
||||
|
||||
## Complete Code Example
|
||||
|
||||
```python
|
||||
import math
|
||||
import asyncio
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Scene, Urdf, AmbientLight, Movable, PointLight
|
||||
|
||||
# Configure app to serve static files from assets directory
|
||||
app = Vuer(static_root="assets/mini_cheetah")
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session):
|
||||
# Setup scene with lighting
|
||||
session.set @ Scene(
|
||||
AmbientLight(intensity=0.8),
|
||||
Movable(
|
||||
PointLight(
|
||||
intensity=2.0,
|
||||
position=[1, 2, 1],
|
||||
key="point-light-1",
|
||||
)
|
||||
),
|
||||
Movable(
|
||||
PointLight(
|
||||
intensity=2.0,
|
||||
position=[-1, 2, -1],
|
||||
key="point-light-2",
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
# Animation loop
|
||||
for i in range(1000):
|
||||
# Calculate joint angles using sine waves
|
||||
hip_angle = 0.5 * math.sin(i * 0.1)
|
||||
thigh_angle = 0.785 - 0.25 * math.sin(i * 0.1)
|
||||
calf_angle = -1.5 + 0.5 * math.sin(i * 0.1)
|
||||
|
||||
# Update robot with animated joints
|
||||
session.upsert @ Urdf(
|
||||
src="/static/mini_cheetah.urdf",
|
||||
position=[0, 0, 0],
|
||||
rotation=[0, 0, 0],
|
||||
jointValues={
|
||||
# Front Left leg
|
||||
"FL_hip_joint": hip_angle,
|
||||
"FL_thigh_joint": thigh_angle,
|
||||
"FL_calf_joint": calf_angle,
|
||||
|
||||
# Front Right leg
|
||||
"FR_hip_joint": -hip_angle,
|
||||
"FR_thigh_joint": thigh_angle,
|
||||
"FR_calf_joint": calf_angle,
|
||||
|
||||
# Rear Left leg
|
||||
"RL_hip_joint": hip_angle,
|
||||
"RL_thigh_joint": thigh_angle,
|
||||
"RL_calf_joint": calf_angle,
|
||||
|
||||
# Rear Right leg
|
||||
"RR_hip_joint": -hip_angle,
|
||||
"RR_thigh_joint": thigh_angle,
|
||||
"RR_calf_joint": calf_angle,
|
||||
},
|
||||
key="mini-cheetah",
|
||||
)
|
||||
|
||||
# Update at ~60 FPS
|
||||
await asyncio.sleep(0.016)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Joint Control
|
||||
|
||||
The Mini Cheetah has 12 joints (3 per leg):
|
||||
|
||||
- **Hip joint**: Abduction/adduction (side-to-side movement)
|
||||
- **Thigh joint**: Hip flexion/extension (forward/backward)
|
||||
- **Calf joint**: Knee flexion/extension
|
||||
|
||||
## Animation Details
|
||||
|
||||
The example creates a walking motion using sinusoidal functions:
|
||||
|
||||
```python
|
||||
hip_angle = 0.5 * math.sin(i * 0.1)
|
||||
thigh_angle = 0.785 - 0.25 * math.sin(i * 0.1)
|
||||
calf_angle = -1.5 + 0.5 * math.sin(i * 0.1)
|
||||
```
|
||||
|
||||
## Expected Result
|
||||
|
||||
When executed correctly, the tutorial produces a 3D visualization of the Mini Cheetah robot with animated leg movements displayed in a web interface at `http://localhost:8012`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Robot not appearing
|
||||
- Verify all mesh files are downloaded
|
||||
- Check that `static_root` points to the correct directory
|
||||
- Ensure URDF file references correct mesh paths
|
||||
|
||||
### Animation not smooth
|
||||
- Adjust the sleep interval (currently 0.016s for ~60 FPS)
|
||||
- Reduce animation speed by changing the multiplier in `i * 0.1`
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/robotics/urdf_mini_cheetah.html
|
||||
134
docs/tutorials/robotics/using-urdf.md
Normal file
134
docs/tutorials/robotics/using-urdf.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Using URDF Files in Vuer
|
||||
|
||||
## Overview
|
||||
|
||||
Vuer enables loading URDF (Unified Robot Description Format) files for robotics visualization. The framework supports mesh files in `.dae`, `.stl`, `.obj`, and `.ply` formats.
|
||||
|
||||
## Basic Implementation
|
||||
|
||||
The code example demonstrates loading URDF models:
|
||||
|
||||
```python
|
||||
from asyncio import sleep
|
||||
from vuer import Vuer, VuerSession
|
||||
from vuer.schemas import Urdf
|
||||
|
||||
app = Vuer()
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(proxy: VuerSession):
|
||||
proxy.upsert @ Urdf(
|
||||
src="https://docs.vuer.ai/en/latest/_static/perseverance/rover/m2020.urdf",
|
||||
jointValues={},
|
||||
rotation=[3.14 / 2, 0, 0],
|
||||
position=[0, 0, -1.5],
|
||||
key="perseverance",
|
||||
)
|
||||
|
||||
# Keep the session alive
|
||||
while True:
|
||||
await sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Key Parameters
|
||||
|
||||
### src
|
||||
URL path to the URDF file. Can be a local path or remote URL.
|
||||
|
||||
### jointValues
|
||||
Dictionary for joint configuration. Use empty `{}` for default joint positions.
|
||||
|
||||
Example with joint values:
|
||||
```python
|
||||
jointValues={
|
||||
"joint_1": 0.5,
|
||||
"joint_2": -0.3,
|
||||
"knee_joint": 1.2,
|
||||
}
|
||||
```
|
||||
|
||||
### rotation
|
||||
Euler angles `[x, y, z]` for model orientation in radians.
|
||||
|
||||
### position
|
||||
3D coordinates `[x, y, z]` for model placement.
|
||||
|
||||
### key
|
||||
Unique identifier for the model. Used for updates and removal.
|
||||
|
||||
## Supported Mesh Formats
|
||||
|
||||
Vuer supports URDF files with mesh files in the following formats:
|
||||
- `.dae` (COLLADA)
|
||||
- `.stl` (STereoLithography)
|
||||
- `.obj` (Wavefront OBJ)
|
||||
- `.ply` (Polygon File Format)
|
||||
|
||||
## Complete Example with Multiple Robots
|
||||
|
||||
```python
|
||||
from asyncio import sleep
|
||||
from vuer import Vuer, VuerSession
|
||||
from vuer.schemas import Urdf, Scene, AmbientLight
|
||||
|
||||
app = Vuer()
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session: VuerSession):
|
||||
session.set @ Scene(
|
||||
# Add lighting
|
||||
AmbientLight(intensity=1.0),
|
||||
|
||||
# Load first robot
|
||||
Urdf(
|
||||
src="/static/robots/robot1.urdf",
|
||||
position=[0, 0, 0],
|
||||
rotation=[0, 0, 0],
|
||||
key="robot-1",
|
||||
),
|
||||
|
||||
# Load second robot
|
||||
Urdf(
|
||||
src="/static/robots/robot2.urdf",
|
||||
position=[2, 0, 0],
|
||||
rotation=[0, 0, 0],
|
||||
key="robot-2",
|
||||
),
|
||||
)
|
||||
|
||||
# Keep the session alive
|
||||
while True:
|
||||
await sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Serving Local URDF Files
|
||||
|
||||
To serve URDF files from your local filesystem:
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
|
||||
# Point to the directory containing your URDF files
|
||||
app = Vuer(static_root="path/to/urdf/directory")
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session):
|
||||
session.upsert @ Urdf(
|
||||
src="/static/my_robot.urdf", # Relative to static_root
|
||||
position=[0, 0, 0],
|
||||
key="my-robot",
|
||||
)
|
||||
|
||||
while True:
|
||||
await sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/robotics/urdf.html
|
||||
Reference in New Issue
Block a user