开发ROS相关的web应用(二) - 在web中与turtlebot仿真互动
本文介绍如何用开发基于angular的应用,结合rosbridge和turtlebot3进行交互。
本文所用的环境是ubuntu20.04 (ros-noetic),angular12
一. turtlebot3的仿真
在ROS上安装turtlebot3仿真环境:
首先安装基本pkg:
$ cd ~/catkin_ws/src/ $ git clone https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git -b noetic-devel $ git clone https://github.com/ROBOTIS-GIT/turtlebot3.git -b noetic-devel $ cd ~/catkin_ws && catkin_make
之后安装sim:
$ cd ~/catkin_ws/src/ $ git clone https://github.com/ROBOTIS-GIT/turtlebot3_simulations.git $ cd ~/catkin_ws && catkin_make
需要以下步骤来配置下系统环境:
source /opt/ros/noetic/setup.bash source /home/akoubaa/catkin_ws/devel/setup.bash export TURTLEBOT3_MODEL=waffle export SVGA_VGPU10=0
其中SVGA_VGPU10是针对Nvidia显卡的选项,如果仍然有显示错误,更新显卡驱动或尝试将值改成1。
开启turtlebot3的gazebo仿真:
$ roslaunch turtlebot3_gazebo turtlebot3_house.launch
二. Ros消息的发布
本节介绍用angular遥控器组件开发一个turtlebot3遥控器
首先安装遥控器的package:
npm install --save-dev ngx-joystick
所安装的angular控件是一个摇杆,支持三种遥感模式,这里示例static模式遥感的使用和ros消息的发布:
html template开发如下:
1 <div> 2 <div class="container noselect"> 3 <div class="zone"> 4 <ngx-joystick #staticJoystic 5 [options]="staticOptions" 6 (start)="onStartStatic($event)" 7 (end)="onEndStatic($event)" 8 (move)="onMoveStatic($event)" 9 (plainUp)="onPlainUpStatic($event)" 10 (plainDown)="onPlainDownStatic($event)" 11 (plainLeft)="onPlainLeftStatic($event)" 12 (plainRight)="onPlainRightStatic($event)"></ngx-joystick> 13 </div> 14 <div *ngIf="staticOutputData && showInfo" class="zone-absolute"> 15 <div style="float: right;"> 16 <div>xPos: {{ staticOutputData.position.x | number:'3.2-2'}}</div> 17 <div>yPos: {{ staticOutputData.position.y | number:'3.2-2'}}</div> 18 <div>dirEvent: {{ directionStatic }}</div> 19 <div>interact: {{ interactingStatic }}</div> 20 </div>ng 21 <div>frontXPos: {{ staticOutputData.instance.frontPosition.x | number:'2.5-5'}}</div> 22 <div>frontYPos: {{ staticOutputData.instance.frontPosition.y | number:'2.5-5'}}</div> 23 <div>force: {{ staticOutputData.force | number:'1.5-5'}}</div> 24 <div>angle-deg: {{ staticOutputData.angle.degree | number:'3.5-5'}}</div> 25 <div>angle-rad: {{ staticOutputData.angle.radian }}</div> 26 <div>direction: {{ staticOutputData.direction ? staticOutputData.direction.angle : ''}}</div> 27 <div>distance: {{ staticOutputData.distance }}</div> 28 </div> 29 </div> 30 </div>
遥控器逻辑:
1 import {Component, OnInit, ViewChild, Output, EventEmitter} from '@angular/core'; 2 import { JoystickEvent, NgxJoystickComponent } from 'ngx-joystick'; 3 import {Joystick, JoystickManagerOptions, JoystickOutputData, Position} from 'nipplejs'; 4 5 @Component({ 6 selector: 'app-teleop', 7 templateUrl: './teleop.component.html', 8 styleUrls: ['./teleop.component.css'] 9 }) 10 export class TeleopComponent implements OnInit { 11 12 @ViewChild('staticJoystic') staticJoystick: NgxJoystickComponent | undefined; 13 14 @Output() moveEvent = new EventEmitter<JoystickOutputData>(); 15 16 staticOptions: JoystickManagerOptions = { 17 mode: 'static', 18 position: { left: '50%', top: '50%' }, 19 color: '#222222', 20 }; 21 22 staticOutputData: JoystickOutputData | undefined; 23 24 directionStatic: string; 25 interactingStatic: boolean; 26 showInfo: boolean = false; 27 28 constructor() { 29 this.directionStatic = ''; 30 this.interactingStatic = false; 31 } 32 33 ngOnInit() { 34 } 35 36 onStartStatic(event: JoystickEvent) { 37 this.interactingStatic = true; 38 } 39 40 onEndStatic(event: JoystickEvent) { 41 if (this.staticOutputData) { 42 let data = this.staticOutputData; 43 data.vector.x = 0; 44 data.vector.y = 0; 45 this.moveEvent.emit(data); 46 } 47 this.interactingStatic = false; 48 } 49 50 onMoveStatic(event: JoystickEvent) { 51 this.staticOutputData = event.data; 52 this.moveEvent.emit(this.staticOutputData); 53 } 54 55 onPlainUpStatic(event: JoystickEvent) { 56 this.directionStatic = 'UP'; 57 } 58 59 onPlainDownStatic(event: JoystickEvent) { 60 this.directionStatic = 'DOWN'; 61 } 62 63 onPlainLeftStatic(event: JoystickEvent) { 64 this.directionStatic = 'LEFT'; 65 } 66 67 onPlainRightStatic(event: JoystickEvent) { 68 this.directionStatic = 'RIGHT'; 69 } 70 71 }
三. Ros消息的监听
这里监视turtlebot3 gazebo中的速度和位置:
为了正确显示角度,首先需要安装THREE这个库来将四元数转化成欧拉角:
npm install --save-dev three
html template:
1 <h3 class="text-center">Robot Position</h3> 2 <div class="row"> 3 <div class="col"> 4 <h4 class="mt-4">Position</h4> 5 <p class="mt-0">x:{{this.state.x}}</p> 6 <p class="mt-0">y:{{this.state.y}}</p> 7 <p class="mt-0">orientation:{{this.state.orientation}}</p> 8 </div> 9 <div class="col"> 10 <h4 class="mt-4">Velocities</h4> 11 <p class="mt-0">Linear Velocity:{{this.state.linear_velocity}}</p> 12 <p class="mt-0">Angular Velocity:{{this.state.angular_velocity}}</p> 13 </div> 14 </div>
逻辑:
1 import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core'; 2 import {JoystickOutputData} from "nipplejs"; 3 import * as THREE from "three"; 4 5 declare var ROSLIB: any; 6 declare var ROS2D: any; 7 8 @Component({ 9 selector: 'app-robot-state', 10 templateUrl: './robot-state.component.html', 11 styleUrls: ['./robot-state.component.css'] 12 }) 13 export class RobotStateComponent implements OnInit, OnDestroy { 14 15 @Input() ros: any = null; 16 17 intervalId: number = 0; 18 19 pose_subscriber: any = null; 20 21 odom_subscriber: any = null; 22 23 position: any = null; 24 25 odom: any = null; 26 27 state = { 28 x: 0, 29 y: 0, 30 orientation: '0', 31 linear_velocity: 0, 32 angular_velocity: 0, 33 } 34 35 constructor() { 36 37 } 38 39 ngOnInit(): void { 40 this.intervalId = setInterval(()=> { 41 if (this.position){ 42 this.state.x = this.position.pose.pose.position.x.toFixed(2); 43 this.state.y = this.position.pose.pose.position.y.toFixed(2); 44 this.state.orientation = (RobotStateComponent.getOrientationFromQuaternion(this.position.pose.pose.orientation)).toFixed(2); 45 } 46 if (this.odom) { 47 this.state.linear_velocity = this.odom.twist.twist.linear.x.toFixed(2); 48 this.state.angular_velocity = this.odom.twist.twist.angular.z.toFixed(2); 49 } 50 }, 100); 51 52 if (this.ros) { 53 this.pose_subscriber = new ROSLIB.Topic({ 54 ros: this.ros, 55 name: "/amcl_pose", 56 messageType: '/geometry_msgs/PoseWithCovarianceStamped', 57 }); 58 59 this.pose_subscriber.subscribe((message: any) => { 60 this.position = message; 61 }); 62 63 this.odom_subscriber = new ROSLIB.Topic({ 64 ros: this.ros, 65 name: "/odom", 66 messageType: 'nav_msgs/Odometry', 67 }); 68 69 this.odom_subscriber.subscribe((message: any) => { 70 this.odom = message; 71 }); 72 } 73 } 74 75 ngOnDestroy() { 76 clearInterval(this.intervalId); 77 } 78 79 private static getOrientationFromQuaternion(ros_orientation_quaternion: any): number { 80 const q = new THREE.Quaternion(ros_orientation_quaternion.x, ros_orientation_quaternion.y, 81 ros_orientation_quaternion.z, ros_orientation_quaternion.w); 82 const RPY = new THREE.Euler().setFromQuaternion(q); 83 return RPY.z * (180 / Math.PI); 84 } 85 86 }
遥控器和监控panel效果如下:
四. 基于ROS2D的地图显示和导航
启动Rviz和导航功能:
$ roslaunch turtlebot3_navigation turtlebot3_navigation.launch map_file:=/your/path/to/map/tb3_house_map.yaml
安装ros2d:
npm install --save-dev ros2d
这里要注意ros2d的两个依赖并不会自动安装:easeljs 和 eventemitter2,分别到相应的git地址中找到相应的代码并放入当前项目中。要实现地图显示和导航还需要nav2d,也从git中找到代码:
在angular.json中添加js代码:
开发地图component,代码如下:
html 模板:
<div id="nav_div"></div>
逻辑代码:
1 import {Component, Input, OnInit} from '@angular/core'; 2 //import {ROS2D} from "../connection.component"; 3 4 declare var ROS2D: any; 5 declare var NAV2D: any; 6 7 @Component({ 8 selector: 'app-map', 9 templateUrl: './map.component.html', 10 styleUrls: ['./map.component.css'] 11 }) 12 export class MapComponent implements OnInit { 13 14 @Input() ros: any = null; 15 16 constructor() { } 17 18 ngOnInit(): void { 19 this.viewMap(); 20 } 21 22 viewMap() { 23 const viewer = new ROS2D.Viewer({ 24 divID: "nav_div", 25 width: 640, 26 height: 480, 27 }); 28 console.log(viewer); 29 const navCli = new NAV2D.OccupancyGridClientNav({ 30 ros: this.ros, 31 rootObject: viewer.scene, 32 viewer: viewer, 33 serverName: "/move_base", 34 widthOrientation: true, 35 }); 36 } 37 38 }
效果:
注:本文中所有component的ROS object都是从主页面中注入的,可以确保所有模块所用ROS对象的一致性
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具