cad2025 電腦輔助設計與實習

  • Home
    • SMap
    • reveal
    • blog
  • About
    • Mind-Map
    • AI
      • Teams
      • Prompts
    • Homework
      • HW2
      • Plotter
  • Topics
    • Network
    • ICMP
    • IPv6
    • DNS
    • Proxy
    • Web
      • Codespaces
  • Software
    • Git
    • CMSiMDE
      • Flask
    • Solvespace
      • Compile
    • NX2312
      • License
    • Onshape
    • Webots
      • Fourbar
      • TimeStep
      • Clouds
      • Talks
      • Blender
      • Ardupilot
  • Tutorial
    • Stage1
      • Tutorial1
      • Tutorial2
    • Stage2
      • Tutorial3
      • Distancesensor
      • Tutorial4
      • Tutorial5
    • Stage3
      • Tutorial6
      • Tutorial7
      • Stream
      • Webots Server
  • Projects
    • Control
    • Printer
    • Otto
    • Otto_ninja-1
      • Simplify
    • OpenDuck
    • Pupper
    • JetAcker
  • Brython
  • Ref
    • Reeborg
      • ex1
      • Otto_ninja-2
    • Pyodide
    • Pyodide_ex
    • Pyodide2
      • robot.py
      • Example2
    • Pyodide3
      • png_files
      • Harvest
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

Copyright © All rights reserved | This template is made with by Colorlib