从零搭建一个ROS2 Web控制面板:用roslibjs实现话题订阅与发布(附完整HTML/CPP代码)

发布时间:2026/5/21 0:31:27

从零搭建一个ROS2 Web控制面板:用roslibjs实现话题订阅与发布(附完整HTML/CPP代码) 从零搭建一个ROS2 Web控制面板用roslibjs实现话题订阅与发布附完整HTML/CPP代码机器人操作系统ROS2的普及让开发者能够更高效地构建复杂的机器人应用。而将机器人状态可视化或通过浏览器进行远程控制则是许多实际项目中的常见需求。本文将手把手教你如何构建一个功能完整的Web控制面板实现ROS2与Web浏览器的双向通信。1. 环境准备与工具链配置1.1 ROS2基础环境确保已安装ROS2 Humble或更新的版本。对于Ubuntu用户推荐使用以下命令验证安装source /opt/ros/humble/setup.bash ros2 doctor如果尚未安装ROS2可参考官方文档进行完整安装。建议选择完整桌面版Desktop-Full以获得所有必要组件。1.2 安装rosbridge_suiterosbridge_suite是连接Web与ROS2的关键桥梁通过WebSocket协议实现通信。安装命令如下sudo apt install ros-humble-rosbridge-suite验证安装是否成功ros2 pkg list | grep rosbridge1.3 获取前端JavaScript库我们需要三个核心JavaScript库roslibjsROS基础功能库EventEmitter2事件处理依赖库ros2djs/ros3djs可选2D/3D可视化推荐直接从GitHub克隆最新版本git clone https://github.com/RobotWebTools/roslibjs.git git clone https://github.com/EventEmitter2/EventEmitter2.git2. ROS2节点开发2.1 创建计数发布者节点新建minimal_publisher.cpp文件实现一个简单的计数器发布者#include rclcpp/rclcpp.hpp #include std_msgs/msg/string.hpp using namespace std::chrono_literals; class CounterPublisher : public rclcpp::Node { public: CounterPublisher() : Node(counter_publisher), count_(0) { publisher_ this-create_publisherstd_msgs::msg::String( /counter, 10); timer_ this-create_wall_timer( 500ms, std::bind(CounterPublisher::timer_callback, this)); } private: void timer_callback() { auto message std_msgs::msg::String(); message.data Count: std::to_string(count_); RCLCPP_INFO(this-get_logger(), Publishing: %s, message.data.c_str()); publisher_-publish(message); } rclcpp::TimerBase::SharedPtr timer_; rclcpp::Publisherstd_msgs::msg::String::SharedPtr publisher_; size_t count_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_sharedCounterPublisher()); rclcpp::shutdown(); return 0; }2.2 创建速度控制订阅者节点新建velocity_subscriber.cpp文件接收来自Web的速度指令#include rclcpp/rclcpp.hpp #include geometry_msgs/msg/twist.hpp class VelocitySubscriber : public rclcpp::Node { public: VelocitySubscriber() : Node(velocity_subscriber) { subscription_ this-create_subscriptiongeometry_msgs::msg::Twist( /cmd_vel, 10, std::bind(VelocitySubscriber::topic_callback, this, std::placeholders::_1)); } private: void topic_callback(const geometry_msgs::msg::Twist::SharedPtr msg) const { RCLCPP_INFO(this-get_logger(), Received velocity command:); RCLCPP_INFO(this-get_logger(), Linear: x%.2f, y%.2f, z%.2f, msg-linear.x, msg-linear.y, msg-linear.z); RCLCPP_INFO(this-get_logger(), Angular: x%.2f, y%.2f, z%.2f, msg-angular.x, msg-angular.y, msg-angular.z); } rclcpp::Subscriptiongeometry_msgs::msg::Twist::SharedPtr subscription_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_sharedVelocitySubscriber()); rclcpp::shutdown(); return 0; }3. Web前端开发3.1 基础HTML结构创建control_panel.html文件构建基本的Web界面框架!DOCTYPE html html langen head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleROS2 Web Control Panel/title style body { font-family: Arial, sans-serif; margin: 20px; } .panel { border: 1px solid #ddd; padding: 15px; margin-bottom: 20px; border-radius: 5px; } button { padding: 8px 15px; margin-right: 10px; cursor: pointer; } .status { color: #666; font-style: italic; } /style /head body h1ROS2 Web Control Panel/h1 div classpanel h2Connection Status/h2 p idconnection-status classstatusDisconnected/p button idconnect-btnConnect/button button iddisconnect-btnDisconnect/button /div div classpanel h2Counter Monitor/h2 p idcounter-displayNo data received/p button idsubscribe-counter-btnSubscribe/button button idunsubscribe-counter-btnUnsubscribe/button /div div classpanel h2Velocity Control/h2 div labelLinear X: input typerange idlinear-x min-1 max1 step0.1 value0/label span idlinear-x-value0/span /div button idpublish-velocity-btnPublish Velocity/button /div script srclib/eventemitter2.js/script script srclib/roslib.min.js/script script srccontrol_panel.js/script /body /html3.2 JavaScript交互逻辑创建control_panel.js文件实现核心通信逻辑// ROS连接配置 const ros new ROSLIB.Ros({ url: ws://localhost:9090 }); // 连接状态处理 ros.on(connection, () { document.getElementById(connection-status).textContent Connected; document.getElementById(connection-status).style.color green; }); ros.on(error, (error) { document.getElementById(connection-status).textContent Error: error; document.getElementById(connection-status).style.color red; }); ros.on(close, () { document.getElementById(connection-status).textContent Disconnected; document.getElementById(connection-status).style.color gray; }); // 连接/断开按钮事件 document.getElementById(connect-btn).addEventListener(click, () { ros.connect(ws://localhost:9090); }); document.getElementById(disconnect-btn).addEventListener(click, () { ros.close(); }); // 计数器订阅 const counterTopic new ROSLIB.Topic({ ros: ros, name: /counter, messageType: std_msgs/String }); document.getElementById(subscribe-counter-btn).addEventListener(click, () { counterTopic.subscribe((message) { document.getElementById(counter-display).textContent message.data; }); }); document.getElementById(unsubscribe-counter-btn).addEventListener(click, () { counterTopic.unsubscribe(); }); // 速度控制发布 const cmdVelTopic new ROSLIB.Topic({ ros: ros, name: /cmd_vel, messageType: geometry_msgs/Twist }); document.getElementById(linear-x).addEventListener(input, (e) { document.getElementById(linear-x-value).textContent e.target.value; }); document.getElementById(publish-velocity-btn).addEventListener(click, () { const linearX parseFloat(document.getElementById(linear-x).value); const twist new ROSLIB.Message({ linear: { x: linearX, y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0 } }); cmdVelTopic.publish(twist); console.log(Published velocity command:, twist); });4. 系统集成与测试4.1 启动rosbridge服务器ros2 launch rosbridge_server rosbridge_websocket_launch.xml4.2 启动ROS2节点新建终端窗口启动计数发布者ros2 run your_package counter_publisher另开终端窗口启动速度订阅者ros2 run your_package velocity_subscriber4.3 访问Web界面将HTML和JS文件放置在Web服务器目录中或直接使用浏览器打开HTML文件。点击Connect按钮建立连接后点击Subscribe按钮开始接收计数器数据拖动滑块调整速度值点击Publish Velocity发送速度指令4.4 调试技巧检查连接状态在浏览器控制台查看WebSocket连接日志验证话题通信使用ros2 topic list和ros2 topic echo命令性能优化对于高频数据考虑使用压缩或降低发布频率5. 进阶功能扩展5.1 添加3D可视化集成ros3djs库实现机器人模型可视化script srclib/ros3d.js/script div idrobot3d stylewidth: 500px; height: 400px;/div script const viewer new ROS3D.Viewer({ divID: robot3d, width: 500, height: 400, antialias: true }); // 添加URDF模型 const urdfClient new ROS3D.UrdfClient({ ros: ros, tfClient: new ROSLIB.TFClient({ ros: ros }), rootObject: viewer.scene, path: http://example.com/urdf/, loader: new ROS3D.COLLADA_LOADER() }); /script5.2 实现服务调用添加服务调用功能示例// 服务定义 const addTwoInts new ROSLIB.Service({ ros: ros, name: /add_two_ints, serviceType: example_interfaces/srv/AddTwoInts }); // 服务调用 function callService() { const request new ROSLIB.ServiceRequest({ a: parseInt(document.getElementById(num-a).value), b: parseInt(document.getElementById(num-b).value) }); addTwoInts.callService(request, (result) { document.getElementById(service-result).textContent result.sum; }); }5.3 安全加固措施身份验证配置rosbridge的认证参数HTTPS/WSS在生产环境使用安全协议输入验证前端对发布的数据进行有效性检查6. 项目部署建议6.1 打包为Docker容器创建Dockerfile简化部署FROM ros:humble # 安装依赖 RUN apt-get update apt-get install -y \ ros-humble-rosbridge-suite \ rm -rf /var/lib/apt/lists/* # 复制Web文件 COPY web/ /var/www/html/ # 启动命令 CMD [bash, -c, source /opt/ros/humble/setup.bash \ ros2 launch rosbridge_server rosbridge_websocket_launch.xml \ ros2 run your_package counter_publisher \ ros2 run your_package velocity_subscriber \ apache2-foreground]6.2 性能优化策略消息压缩对大消息启用压缩QoS配置根据需求调整服务质量策略前端节流限制高频UI更新6.3 跨平台适配响应式设计适配移动设备浏览器兼容处理不同浏览器的特性差异离线功能添加Service Worker支持

相关新闻