TimeStep <<
Previous Next >> Talks
Clouds
https://cyberbotics.com/doc/guide/web-streaming
Webots 透過 webotsw --stream 所啟動的串流伺服器, 可以將串流內容導向全球資訊網伺服器, 利用下列超文件透過 Websocket 檢視 Webots 串流場景.
index4.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Streaming viewer</title>
<link rel="icon" type="image/png" href="webots_icon.png">
<link type="text/css" rel="stylesheet" href='style.css' />
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 100vw;
height: 100vh;
min-height: 100vh;
min-width: 100vw;
display: flex;
flex-direction: column;
background: #222;
}
header {
flex: 0 0 auto;
padding: 0;
margin: 0;
background: #181818;
color: #fff;
text-align: center;
}
.title-container {
margin: 0;
padding: 0.5em 0;
}
.webots-view-container {
flex: 1 1 0;
display: flex;
align-items: stretch;
justify-content: stretch;
min-height: 0;
min-width: 0;
}
webots-view {
width: 100vw;
height: 100vh;
flex: 1 1 0;
display: block;
min-width: 0;
min-height: 0;
}
</style>
</head>
<body>
<div class="webots-view-container">
<webots-view></webots-view>
</div>
<script type="module" src="http://localhost:8000/wwi/WebotsView.js"></script>
<script>
// Default WebSocket parameters
const defaultIp = "ws://localhost:1234";
const defaultStreamingMode = "x3d";
const defaultBroadcast = false;
const mobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const defaultThumbnail = './../wwi/images/loading/default_thumbnail.png';
// Optional: load mobile CSS if on mobile device
if (mobileDevice) {
let head = document.getElementsByTagName('head')[0];
let mobileCss = document.createElement('link');
mobileCss.setAttribute('rel', 'stylesheet');
mobileCss.setAttribute('type', 'text/css');
mobileCss.setAttribute('href', './../wwi/css/wwi_mobile.css');
head.appendChild(mobileCss);
}
// Wait for WebotsView.js to load and custom element to be defined
window.addEventListener('DOMContentLoaded', () => {
customElements.whenDefined('webots-view').then(() => {
const webotsView = document.querySelector('webots-view');
webotsView.onready = () => {
console.log("Connected to Webots streaming server!");
};
webotsView.ondisconnect = () => {
console.log("Disconnected from Webots streaming server!");
};
webotsView.connect(defaultIp, defaultStreamingMode, defaultBroadcast, mobileDevice, -1, defaultThumbnail);
});
});
</script>
</body>
</html>
下列 Python 程式執行時則可動態建立近端伺服器, 並直接利用 Edge 開啟 index4.html 中的 Webots 場景.
python_www_server.py
import http.server
import socketserver
import threading
import webbrowser
import time
import os
import sys
import subprocess
PORT = 8000
DIRECTORY = os.path.dirname(os.path.abspath(__file__))
class Handler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIRECTORY, **kwargs)
def open_edge(url):
# Try common Edge paths on Windows
edge_paths = [
r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
r"C:\Program Files\Microsoft\Edge\Application\msedge.exe"
]
for path in edge_paths:
if os.path.exists(path):
subprocess.Popen([path, url])
return True
# Fallback: try using webbrowser module (may open in default browser)
try:
webbrowser.get('windows-default').open(url)
except:
webbrowser.open(url)
return False
def run_server():
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"Serving at http://localhost:{PORT}")
httpd.serve_forever()
if __name__ == "__main__":
# Start server in a thread
server_thread = threading.Thread(target=run_server, daemon=True)
server_thread.start()
# Wait a moment for the server to start
time.sleep(1)
# Open Edge to the local index.html
url = f"http://localhost:{PORT}/streaming_viewer/index4.html"
print(f"Opening {url} in Edge...")
open_edge(url)
# Keep main thread alive to keep server running
try:
while True:
time.sleep(10)
except KeyboardInterrupt:
print("Shutting down.")
sys.exit(0)
webots_stream.7z
在雲端提供多 Webots 場景串流的方法是, 每一個場景採
webotsw my_world.wbt --stream --port 8888
在 8888 埠號串流 my_world.wbt 場景, 並在 WWW 伺服器中指定要開啟的 port 就能利用一台雲端主機檢視多個由使用者上傳的場景. 再加上 https 設定, 建立類似 https://webots.cloud/ 的服務.
cloud (https://github.com/cyberbotics/webots-cloud) 中的 html 如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title id="title">webots.cloud</title>
<meta name="description" content="Run Webots simulations in the cloud">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="og:locale" content="en">
<meta name="og:type" content="website">
<meta name="og:title" content="webots.cloud">
<meta name="og:description" content="The place to share Webots simulations.">
<meta name="og:updated_time" content="1587637793">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/default.min.css"/>
<link rel="stylesheet" href="/css/webots-cloud.css">
</head>
<body>
<div class="scrollable-body" id="scrollable-body"></div>
<script src="/js/webots-cloud.js" type="module"></script>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.3.0/showdown.min.js"></script>
<script src="https://cyberbotics.com/showdown/1.3.0/showdown-youtube.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/7.0.0/mermaidAPI.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/cyberbotics/webots@released/docs/js/showdown-extensions.js"></script>
</html>
在 w_dot_mde 設定:
nginx.conf (主要讓 wss:// 透過 https 反向代理.
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# Redirect HTTP to HTTPS
server {
listen [2001:288:6004:17::XX]:80;
server_name XX.nfu.edu.tw;
return 301 https://$host$request_uri;
}
# HTTPS server
server {
listen [2001:288:6004:17::XX]:443 ssl;
server_name XX..nfu.edu.tw;
ssl_certificate C:/Certbot/live/w.mde.nfu.edu.tw/fullchain.pem;
ssl_certificate_key C:/Certbot/live/w.mde.nfu.edu.tw/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
#root html;
root html/webots_resources_web/;
index index.html index.htm;
}
# Webots streaming server 的 WebSocket 反向代理
location /webots_ws/ {
proxy_pass http://localhost:1234/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /auto_shoot/ {
proxy_pass http://localhost:1235/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
此時的 index.html 設為:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Streaming viewer</title>
<link rel="icon" type="image/png" href="webots_icon.png">
<link type="text/css" rel="stylesheet" href='style.css' />
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 100vw;
height: 100vh;
min-height: 100vh;
min-width: 100vw;
display: flex;
flex-direction: column;
background: #222;
}
header {
flex: 0 0 auto;
padding: 0;
margin: 0;
background: #181818;
color: #fff;
text-align: center;
}
.title-container {
margin: 0;
padding: 0.5em 0;
}
.webots-view-container {
flex: 1 1 0;
display: flex;
align-items: stretch;
justify-content: stretch;
min-height: 0;
min-width: 0;
}
webots-view {
width: 100vw;
height: 100vh;
flex: 1 1 0;
display: block;
min-width: 0;
min-height: 0;
}
</style>
</head>
<body>
<div class="webots-view-container">
<webots-view></webots-view>
</div>
<script type="module" src="./../wwi/WebotsView.js"></script>
<script>
// 根據當前協定與主機自動組成 wss/ws 路徑
const wsProtocol = (window.location.protocol === "https:") ? "wss:" : "ws:";
const wsHost = window.location.host;
// 注意:此處路徑要跟 nginx location /webots_ws/ 一致
const defaultIp = wsProtocol + "//" + wsHost + "/webots_ws/";
const defaultStreamingMode = "x3d";
const defaultBroadcast = false;
const mobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const defaultThumbnail = './../wwi/images/loading/default_thumbnail.png';
// Optional: load mobile CSS if on mobile device
if (mobileDevice) {
let head = document.getElementsByTagName('head')[0];
let mobileCss = document.createElement('link');
mobileCss.setAttribute('rel', 'stylesheet');
mobileCss.setAttribute('type', 'text/css');
mobileCss.setAttribute('href', './../wwi/css/wwi_mobile.css');
head.appendChild(mobileCss);
}
// Wait for WebotsView.js to load and custom element to be defined
window.addEventListener('DOMContentLoaded', () => {
customElements.whenDefined('webots-view').then(() => {
const webotsView = document.querySelector('webots-view');
webotsView.onready = () => {
console.log("Connected to Webots streaming server!");
};
webotsView.ondisconnect = () => {
console.log("Disconnected from Webots streaming server!");
};
webotsView.connect(defaultIp, defaultStreamingMode, defaultBroadcast, mobileDevice, -1, defaultThumbnail);
});
});
</script>
</body>
</html>
auto_shoot.html
Webots headless command:
webots --no-rendering --stream --batch --port=1234 C:\portable_wcm2025_big\data\Webots_2023b\projects\vehicles\worlds\city.wbt
Since Webots runs in headless mode, the --stdout
and --stderr
arguments are used to redirect these streams from the Webots console to the console in which Webots was started, the --batch
argument disables any blocking pop-up window and the --mode=realtime
makes sure that the simulation is not started in pause mode (you may replace realtime
by fast
), finally don't forget to specify which simulation you want to run.
https://www.cyberbotics.com/doc/guide/installation-procedure?version=develop#installing-the-docker-image
https://gist.github.com/cyberang3l/422a77a47bdc15a0824d5cca47e64ba2
與可攜系統同時啟動:
@echo off
set Disk=y
subst %Disk%: "data"
%Disk%:
set HomePath=%Disk%:\home_ipv6
set HomeDrive=%Disk%:\home_ipv6
set Home=%Disk%:\home_ipv6
set USERPROFILE=%Disk%:\home_ipv6
REM 將系統 Python 程式的 io 設為 utf-8
set PYTHONIOENCODING="utf-8"
set PYTHONPATH=%Disk%:\Python313\DLLs;%Disk%:\Python313\Lib;%Disk%:\Python313\Lib\site-packages;%Disk%:\NX\NXBIN\python;%Disk%:\Webots_2023b\msys64\mingw64\bin;%Disk%:\Webots_2023b\lib\controller\python;
set PYTHONHOME=%Disk%:\Python313
REM for Webots
set PYTHON_PATH=%Disk%:\Python313
set WEBOTS_HOME=%Disk%:\Webots_2023b
REM for putty
set GIT_SSH=%Disk%:\PuTTY\plink.exe
REM for Java and Android SDK
set java_home=%Disk%:\java\jdk8u222-b10
set ANDROID_SDK_home=%Disk%:\home_ipv6
set GRADLE_USER_home=%Disk%:\home_ipv6
set ANDROID_SDK_ROOT=%Disk%:\android\sdk
set ANDROID_Home=%Disk%:\android\sdk
set REPO_OS_OVERRIDE=windows
REM 設定跟 Python 有關的命令搜尋路徑
set path_python=%Disk%:\Python313;%Disk%:\Python313\Scripts;
REM 設定跟Git 有關的命令搜尋路徑
set path_git=%Disk%:\portablegit\bin;
REM 設定 msys2 64 位元的執行路徑
REM %Disk%:\msys64\mingw64\bin is for GD library
set path_msys2=%Disk%:\msys64\ucrt64\bin;%Disk%:\msys64\mingw64\bin;
REM set for LaTeX
set path_miketex=%Disk%:\miktex-portable\texmfs\install\miktex\bin\x64;
REM Flutter path
set path_flutter=%Disk%:\flutter\bin;%java_home%\bin;%Disk%:\Android\sdk;%Disk%:\Android\sdk\tools;%Disk%:\Android\sdk\tools\bin;%Disk%:\Android\sdk\emulator;%Disk%:\Android\sdk\platform-tools;%Disk%:\flutter\bin\cache\dart-sdk\bin;%Disk%:\vscode;
set path_node=%Disk%:\node-v14.15.4-win-x64;
set path_postgresql=%Disk%:\postgresql13\bin;
set path_range=%Disk%:\Range3_official\bin;
rem set path_range=%Disk%:\range-3.2.5_kmol\bin;
set path_lua=%Disk%:\lua-5.3.5\;
set path_core=%Disk%:\coreutils-5.3.0\bin;
REM for gogs
set path_putty=%Disk%:\PuTTY;
set path_vscode=%Disk%:\vscode;
REM for Rust
set MINGW_PATH=%path_msys2%;
set GIT_PATH=%path_git%;
set VSCODE_PATH=%path_vscode%
set RUSTUP_HOME=%Disk%:\Rust\rust
set CARGO_HOME=%Disk%:\Rust\cargo
set RUST_PATH=%CARGO_HOME%\bin
REM for Erlang and Elixir
set path_erlang=%Disk%:\elixir\erl10.7\bin\;%Disk%:\elixir\erl10.7\erts-10.7\bin;%Disk%:\elixir\elixir\bin;
set ERTSPATH=%Disk%:\elixir\erl10.7\erts-10.7\bin
set ERLINI=%Disk%:\elixir\erl10.7\erts-10.7\bin\erl.ini
REM for Haskell
set path_haskell=%Disk%:\ghc-9.0.1-x86_64-unknown-mingw32\bin;
REM for node.js
set path_nodejs=%Disk%:\node-v14.17.2-x64\nodejs;%Disk%:\node-v14.17.2-x64\nodejs\node_modules\npm;
REM 加入 PostgreSQL 所需的環境變數設定
@SET PGDATA=%Disk%:\postgresql13\data
@SET PGDATABASE=postgres
@SET PGUSER=postgres
@SET PGPORT=5432
@SET PGLOCALEDIR=%Disk%:\postgresql13\share\locale
REM for Range3
REM REG IMPORT %Disk%:\range3.reg
REM proxy needed for heroku login
REM proxy can not use with fossil scm
REM proxy can not use with Rust installation
REM needed for node.js
REM set HTTP_PROXY=http://[2001:288:6004:xx::42]:3128
REM set HTTPS_PROXY=http://[2001:288:6004:xx::42]:3128
REM for PyRep
set VREP_PATH=%Disk%:\CoppeliaSim
REM BOOST_ROOT for compiling coppeliasim but maybe not for linking
REM set BOOST_ROOT=%Disk%:\boost_1_76_0\;
set path_copsim=%Disk%:\boost_1_76_0\;%Disk%:\Strawberry\perl\bin;%Disk%:\cmake-3.21.0-windows-x86_64\bin;%Disk%:\diffutils-2.8.7-1\bin;%Disk%:\patch-2.5.9-7\bin;%D%:\jom_1_1_2;%Disk%:\LLVM\bin;%Disk%:\QtCreator\bin;%Disk%:\CoppeliaSimEdu;%Disk%:\Python313\tcl\tcl8.6;
REM for CMake
set CMAKE_C_COMPILER=%Disk%:\msys64\ucrt64\bin\gcc;
set CMAKE_CXX_COMPILER=%Disk%:\msys64\ucrt64\bin\g++;
REM set QT_QPA_PLATFORM_PLUGIN_PATH=Y:\msys64_20240507\mingw64\share\qt5\plugins\platforms
REM set QT_PLUGIN_PATH=Y:\msys64_20240507\mingw64\share\qt5\plugins
REM for execute scite directly
set path_scite=%Disk%:\wscite\;
REM for simExtZMQ
set path_xsltproc=%Disk%:\xsltproc;
REM for OpenSSL
Set OPENSSL_CONF=%Disk%:\OpenSSL-Win64\SSL\openssl.cnf
set path_openssl=%Disk%:\OpenSSL-Win64\bin;
REM for textract
set path_poppler=%Disk%:\poppler-24.07.0\Library\bin;
REM for tiny C compiler
set path_tcc=%Disk%:\tcc;
REM for webots
set path_webots=%Disk%:\Webots_2023b\msys64\mingw64\bin;%Disk%:\Webots_2023b\msys64\usr\bin
REM for blender
set path_blender=%Disk%:\Blender 4.2;
path=%Disk%:;%path_python%;%path_git%;%path_copsim%;%path_msys2%;%path_miketex%;%path_flutter%;%path_node%;%path_tcc%;%path_postgresql%;%path_range%;%path_lua%;%path_core%;%path_putty%;%path_vscode%;%RUST_PATH%;%path_erlang%;%path_nodejs%;%path_haskell%;%path_scite%;%path_xsltproc%;%path_gnuplot%;%path_openssl%;%path_poppler%;%path_webots%;%path_blender%;%path%;
REM for NX2312, must after path setup
set SPLM_LICENSE_SERVER=29000@your_server
set UGII_LANG=english
set UGS_LICENSE_BUNDLE=ACD11,ACD10
set UGII_BASE_DIR=%Disk%:\NX\
set UGII_TMP_DIR=%Disk%:\NX\temp
set UGII_LIB_PATH=%Disk%:\NX\NXBIN\python
set ugii=%Disk%:\NX\ugii;%Disk%:\NX\nxbin;
path = %ugii%;%path%
REM Xcopy %Disk%:\home_ipv6\AppData\Local\Siemens\NX2312 C:\users\%USERNAME%\AppData\Local\Siemens\NX2312 /E /H /C /I /Y
REM start ugraf -nx
REM python %Disk%:\tmp\nx_ex\new_part.py
start /MIN cmd.exe
start /MIN cmd.exe
start /MIN cmd.exe
start /MIN cmd.exe
start /MIN %Disk%:\wscite\SciTE.exe
start /MIN %Disk%:\wscite\SciTE.exe
REM start webotsw.exe
start webots.exe --no-rendering --minimize --stream --stdout --stderr --batch --mode=realtime --port=1234 C:\portable_wcm2025_big\data\Webots_2023b\projects\vehicles\worlds\city.wbt
start webots.exe --no-rendering --minimize --stream --stdout --stderr --batch --mode=realtime --port=1235 C:\Webots_server\webots_projects\cd2025_final_project_w17\worlds\w17_auto_loop.wbt
Exit
TimeStep <<
Previous Next >> Talks