概要(前提)
前回までの検証において、ROS2(Jazzy)環境から/cmd_velを用いて、WiFi経由で実機ロボット(Arduino UNO R4 WiFi + タミヤ車体)を制御できることを確認しました。
ROS2(Jazzy)からcmd_velで実機ロボットを制御する統合検証
本検証ではその続きとして、rosbridgeおよびroslibjsを用い、ブラウザから/cmd_velをpublishすることで、Webインターフェース経由で実機ロボットを操作できるかを確認しました。

このようなロボットを自作のROS2ノードとrosbridge_serverを稼働させた状態で以下のような画面から(htmlページ)コントロールします。

構成
本検証の構成は以下の通りです。
ブラウザ(HTML + roslibjs)
↓ WebSocket
rosbridge_server
↓
ROS2(cmd_vel)
↓
ROS2ノード(cmd_vel → コマンド変換)
↓ WiFi
Arduino UNO R4 WiFi
↓
タミヤ車体
実施内容
使用した要素
本検証では以下を使用しました。
- rosbridge_server
- roslibjs(CDN)
- シンプルなHTML操作画面(ボタンによる前後左右・停止)
rosbridge_serverの準備
rosbridgeの導入
rosbridgeはROS2環境から以下のコマンドでインストールできます。
sudo apt update
sudo apt install ros-jazzy-rosbridge-server
rosbridgeの起動
インストール後、以下のコマンドでWebSocketサーバーを起動します。
ros2 launch rosbridge_server rosbridge_websocket_launch.xml
起動すると、デフォルトでポート「9090」で待ち受けを行います。
接続先
ブラウザ側からは、以下の形式で接続します。
ws://<ROS2環境のIPアドレス>:9090
例:
ws://192.168.1.100:9090
■ 補足
- rosbridgeはWebSocket経由でROS2と通信するためのブリッジです
- roslibjsなどのJavaScriptライブラリから、トピックのpublish/subscribeが可能になります
HTML操作画面の用意
roslibjsを利用した簡易的なHTMLページを作成し、ボタン操作により/cmd_velをpublishできるようにしました。
操作としては以下を用意しています。
- 前進
- 後退
- 左旋回
- 右旋回
- 停止
ここでは(AIを利用して生成したHTMLファイルですが、)以下のようなHTMLファイルを利用してテストをしました。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ROS2 手動操作(cmd_vel)</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/roslibjs/1.1.0/roslib.min.js"></script>
<style>
body {
font-family: sans-serif;
background: #f5f5f5;
margin: 0;
display: flex;
flex-direction: column;
height: 100vh;
}
.header {
background: #333;
color: white;
padding: 10px;
}
.container {
display: flex;
flex: 1;
}
.left {
flex: 1;
display: flex;
flex-direction: column;
padding: 10px;
}
.controls {
display: grid;
grid-template-columns: repeat(3, 80px);
gap: 10px;
justify-content: center;
margin-top: 20px;
}
button {
padding: 15px;
font-size: 18px;
cursor: pointer;
border-radius: 5px;
border: none;
background: #3498db;
color: white;
}
button:hover {
background: #2980b9;
}
.stop {
background: #e74c3c;
}
.stop:hover {
background: #c0392b;
}
.log {
margin-top: 20px;
background: #111;
color: #0f0;
padding: 10px;
font-family: monospace;
height: 200px;
overflow-y: auto;
}
.status {
margin-top: 10px;
}
</style>
</head>
<body>
<div class="header">
ROS2 手動操作(cmd_vel)
</div>
<div class="container">
<div class="left">
<div>
ROSBridge URL:
<input id="url" value="ws://192.168.xxx.xxx:9090" style="width:250px;">
<button onclick="connect()">CONNECT</button>
</div>
<div class="status">
Status: <span id="status">DISCONNECTED</span>
</div>
<div class="controls">
<div></div>
<button onclick="forward()">↑</button>
<div></div>
<button onclick="left()">←</button>
<button class="stop" onclick="stopRobot()">■</button>
<button onclick="right()">→</button>
<div></div>
<button onclick="back()">↓</button>
<div></div>
</div>
<div class="log" id="log">
--- LOG ---<br>
</div>
</div>
</div>
<script>
let ros = null;
let cmdVel = null;
function log(msg) {
const area = document.getElementById("log");
const time = new Date().toLocaleTimeString();
area.innerHTML += `[${time}] ${msg}<br>`;
area.scrollTop = area.scrollHeight;
}
function connect() {
const url = document.getElementById("url").value;
ros = new ROSLIB.Ros({ url: url });
ros.on('connection', () => {
document.getElementById("status").innerText = "CONNECTED";
document.getElementById("status").style.color = "green";
log("ROS 接続成功");
cmdVel = new ROSLIB.Topic({
ros: ros,
name: '/cmd_vel',
messageType: 'geometry_msgs/Twist'
});
});
ros.on('error', (err) => {
log("エラー: " + err);
});
ros.on('close', () => {
document.getElementById("status").innerText = "CLOSED";
document.getElementById("status").style.color = "red";
log("接続切断");
});
}
function send(linear, angular, label) {
if (!cmdVel) {
log("⚠ 未接続");
return;
}
const msg = new ROSLIB.Message({
linear: { x: linear, y: 0, z: 0 },
angular: { x: 0, y: 0, z: angular }
});
cmdVel.publish(msg);
log(label);
}
// --- 操作 ---
function forward() { send(1.0, 0.0, "前進"); }
function back() { send(-1.0, 0.0, "後退"); }
function left() { send(0.0, 1.0, "左回転"); }
function right() { send(0.0, -1.0, "右回転"); }
function stopRobot(){ send(0.0, 0.0, "停止"); }
</script>
</body>
</html>
Webサーバーの起動
作成したHTMLファイルは、ローカル環境で簡易Webサーバーを起動して配信しました。
python3 -m http.server 8000
これにより、ブラウザからHTMLページへアクセス可能となります。
rosbridgeとの接続
HTMLページ上から、rosbridge_serverへWebSocket接続を行います。
接続先は以下の形式です。
ws://<ROS2環境のIPアドレス>:9090
接続後、roslibjsを通じて/cmd_velトピックへメッセージをpublishします。
検証結果
HTMLページからボタン操作を行うことで、
/cmd_velのpublish- ROS2ノードによる変換処理
- Arduinoへの送信
- 実機ロボットの動作
が一連の流れとして動作することを確認しました。
また、前回実装した超音波センサーによる距離検知も有効であり、一定距離(約15cm)での停止動作がWeb操作時にも機能することを確認しました。
技術的ポイント
- rosbridgeを利用することで、ブラウザから直接ROS2へアクセス可能
- roslibjsによりJavaScriptから簡単にトピック操作が可能
/cmd_velをインターフェースとして統一することで、操作手段(CLI / Web)の差異を吸収- センサー制御はロボット側で優先処理されるため、安全性を維持
位置づけ(ロボット実機の統合検証②)
本検証は以下の要素を統合した検証として位置付けます。
- ROS2によるロボット制御
- WebSocket通信(rosbridge)
- ブラウザベースの操作インターフェース
- 実機ロボットの駆動
これにより、コマンドライン操作に加えて、Webインターフェースからの操作が可能であることを確認しました。
今後の展開
本構成をベースとして、次の段階として以下を予定しています。
- Blocklyによる動作フロー制御
- 非エンジニアでも扱える操作環境の構築
これにより、操作インターフェースの抽象化と利用範囲の拡張を図ります。
まとめ
本検証により、rosbridgeおよびroslibjsを用いることで、ブラウザからROS2の/cmd_velをpublishし、実機ロボットを操作できることを確認しました。

