python机器人编程——用手机web远程视频监控并控制小车驾驶(上篇vrep仿真)

CSDN 2024-10-01 08:03:00 阅读 84

目录

一、前言二、技术架构三、设备端实现四、服务控制端实现(1)摄像头服务模块(2)web服务器

五、web端实现(1)视频显示(2)驾驶盘的实现(3)心跳

六、总结七、源码或环境PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源ps3.移动小车相关文章资源

一、前言

在现代科技的推动下,机器人技术正逐渐进入我们的生活。而编程作为机器人技术中不可或缺的一环,也成为了众多科技爱好者追逐的目标之一。今天,我将带领大家探索一种新颖而有趣的机器人编程方式——用Python编程实现手机web远程视频监控并控制小车驾驶。

本系列博文将分为上下两篇,分别介绍了在WiFi环境下,如何实现Web端的视频监控和控制远程差速小车的驾驶。上篇将重点介绍如何利用V-REP软件进行仿真车控制,下篇将继续讲解如何实现实物车的控制。

在这个系列博文中,我们将使用Python作为主要编程语言,并利用其强大的库和框架来实现所需功能。而作为Web服务器端,我们将使用Python来搭建服务器,而Web端则使用HTML来实现。

通过这个系列博文的学习,我们将深入了解机器人编程的基本原理和技术,并通过实践来提升自己的编程能力。同时,我们还将学会如何通过手机远程监控和控制机器人,为我们的生活带来更多的便利。

上篇效果如下,左侧时web端可以运行在电脑及手机浏览器,右侧是仿真环境:

在这里插入图片描述

以下是python服务器端:

在这里插入图片描述

二、技术架构

技术架构主要三个部分,底层为硬件(或仿真环境),实现电机,摄像头和传感器及车架等的设计制造,并提供上层的接口。

中层为控制服务端,主要由python实现,可运行在笔记本和工控机,实现与下层的通讯,及与web端的通讯。主要由采集驱动系列模块,数据服务模块,及操控小车相关的逻辑模块,还有web的服务器模块,摄像头视频服务模块及UI管理操作模块组成。

上层web交互端,主要提供web客户端,UI操控界面,及相应的逻辑模块组成。

在这里插入图片描述

三、设备端实现

本篇采用VREP搭建的一个家庭环境并实现小车的活动区域,并加入差速小车配置摄像头,测距雷达等。

在这里插入图片描述

然后提供了与上位机通讯的脚本模块:

在这里插入图片描述

仿真配套资源已经上传:下载地址

四、服务控制端实现

通过python实现服务控制端。本篇主要介绍web及摄像头服务模块。其它模块处理见此前VREP小车相关博文,或者下篇。

(1)摄像头服务模块

摄像头服务模块,采用了一个知名库mjpeg_streamer,可以很容易实现一个局域网的web视频服务。几行代码就可以:

<code>

from mjpeg_streamer import MjpegServer, Stream

from socket import *

import numpy as np

import cv2

cap = cv2.VideoCapture(0)

# 设置镜头分辨率,默认是640x480

cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)

cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

stream = Stream("my_camera", size=(640, 480), quality=50, fps=30)

server = MjpegServer("0.0.0.0", 8000)

server.add_stream(stream)

server.start()

while True:

try:

_, frame = cap.read()

stream.set_frame(frame)

#s.sendto(message.encode(), addr)

#cv2.putText(r_img, "server", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)

cv2.imshow('server', frame)

print("receive")

except BlockingIOError as e:

pass

if cv2.waitKey(1) & 0xFF == ord('q'):

break

cv2.destroyAllWindows()

server.stop()

cap.release()

(2)web服务器

web服务采用了知名的fastapi库,自带websocket功能,也是几行代码就可以实现,这里主要注意的是,为了项目主逻辑运行,web要放到子线程中使用。主线程运行后,其它就不能运行了。

fastapi实现websocket也很简单:

from fastapi import FastAPI, Response, WebSocket, WebSocketDisconnect

from fastapi.responses import HTMLResponse

from fastapi.staticfiles import StaticFiles

from fastapi.templating import Jinja2Templates

#from starlette.websockets import

import socket

import json

app = FastAPI()

# 设置静态文件和模板

app.mount("/static", StaticFiles(directory="static"), name="static")code>

templates = Jinja2Templates(directory="templates")code>

# 获取本机IP地址

def get_local_ip():

try:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

s.connect(("8.8.8.8", 80))

ip = s.getsockname()[0]

finally:

s.close()

return ip

# HTML模板

html_content = """some html"""

@app.get("/", response_class=HTMLResponse)

async def get():

ip = get_local_ip()

return HTMLResponse(html_content+ip+left+ip+tail)

@app.websocket("/ws")

async def websocket_endpoint(websocket: WebSocket):

await websocket.accept()

while True:

try:

data = await websocket.receive_text()

print("receive:",data)

if data=="ping":

await websocket.send_text("pong")

else:

json_data = json.loads(data)

###do something

response_data = { -- -->"code": 200,

"left":round(lv_now,2),

"right":round(rv_now,2)}

await websocket.send_text(json.dumps(response_data))

except WebSocketDisconnect:

manager.disconnect(websocket)

await manager.broadcast("A client disconnected.")

import uvicorn

config = uvicorn.Config(app=app, host="0.0.0.0", port=9000)code>

server = uvicorn.Server(config=config)

singletonThread = threading.Thread(target=server.run)

singletonThread.setDaemon(True)

singletonThread.start()

五、web端实现

web端使用与手机远程操控,主要布局设计如下:

各元素数值排列,适合手机端显示。

在这里插入图片描述

web端主要实现几个元素为:

(1)视频显示

此处采用标签连接mjpeg_streamer的流,实现视频画面的实时获取,在wif的环境下,实时性还是可以的。如web端不在与服务端同一台机器,则localhost改为服务端的ip地址,实现局域网远程获取画面。

<code><img style="-webkit-user-select: none; background-color: hsl(0,8%, 25%);" src="http://localhost:8000/my_camera" width="366*2" height="274*2">code>

在这里插入图片描述

(2)驾驶盘的实现

凭着简洁的思想,驾驶盘采用类似王者等手游的手柄效果,通过拖宅一个焦点在大圆内运动,实现小车两轮速度指令的获取。主要要点是:

1)焦点只能在大圆所在区域运动。

在这里插入图片描述

主要代码如下:

<code> function onMouseMove(e) { -- -->

if (isDragging) {

const centerX = e.clientX - bigCircle.getBoundingClientRect().left - (smallCircle.offsetWidth / 2);

const centerY = e.clientY - bigCircle.getBoundingClientRect().top - (smallCircle.offsetHeight / 2);

clampPosition(centerX, centerY);

updateCoordinates(centerX, centerY);

}

}

function clampPosition(centerX, centerY) {

const maxRadius = bigCircle.offsetWidth / 2;

let r=calculateDistanceFromCenter(centerX, centerY);

let xy;

if (r>maxRadius){

xy=[prev_x,prev_y];

}

else{

xy=[centerX,centerY];

prev_x=centerX;

prev_y=centerY;

}

const x=xy[0];

const y=xy[1];

xx=x;

yy=y;

// 设置小圆的左边距

smallCircle.style.left = `${ x}px`;

// 设置小圆的上边距

smallCircle.style.top = `${ y}px`;

}

2)当手或者鼠标释放时,焦点自动回到原心,同时速度指令缓慢归0,这样的目的是当速度较大行驶时,有个缓冲,不至于突然归0,导致击杀。

在这里插入图片描述

主要代码如下:

<code> function returnToCenter() { -- -->

smallCircle.style.transition = 'all 0.3s ease-out';

smallCircle.style.left = `${ initialX}px`;

smallCircle.style.top = `${ initialY}px`;

decreaseValueR()

}

function decreaseValueR() {

const interval = setInterval(() => {

VL=dvv(VL);

VR=dvv(VR);

console.log(`vv: ${ VV},vl:${ VL},vr:${ VR}`);

if (VR==0 && VL==0){

clearInterval(interval);

console.log('R has reached 0');

}

if (VV > 0) {

VV -= maxV/5;

if(VV<0)

VV=0;

//console.log(`vv: ${VV},vl:${VL},vr:${VR}`);

} else if(VV<0) {

VV += maxV/5;

if(VV>0)

VV=0;

}

sendupdate();

}, 100);

}

3)捕捉小圆的角度,以及小圆离开圆心的距离,并通过一些坐标变换,换算成小车速度,左右轮速分配比例等

此处canvas坐标是x轴在上,向右为正,y轴在左,向下为正。而我们方向盘的习惯是跟常规解析几何坐标系相同,这里需要做一些变换,来计算小圆跟x轴的夹角,实际上求出来夹角正好正负相反。

function calculateAngleWithXAxis(x, y) {

const centerX = initialX;

const centerY = initialY;

const deltaX = x - centerX;

const deltaY = y - centerY;

const angle = -Math.atan2(deltaY, deltaX) * (180 / Math.PI); // 夹角刚好正负相反Convert radians to degrees

return angle;

}

4)设计了左右两个扇形死区。主要作用一个是避免速度在死区内振荡,因为人的手指也有抖动的时候,二个是把原地旋转刚好做到这个驾驶盘里面,实现了简洁性。

死区的处理:

if(a>=5 && a<175){

//go forward

VV=r/maxRadius*maxV;

VR=VV*2*rR;

VL=VV*2*rL;

}else if(a>-175 && a<-5){

//go backword

VV=-r/maxRadius*maxV;

VR=VV*2*rR;

VL=VV*2*rL;

}else if(a<=5&&a>=-5){

VV=r/maxRadius*maxV;

VR=-VV;

VL=VV;

}else if(a<=180&&a>=175){

VV=r/maxRadius*maxV;

VR=VV;

VL=-VV;

}else if(a>=-180&&a<=-175){

VV=r/maxRadius*maxV;

VR=VV;

VL=-VV;

}

(3)心跳

心跳主要是为了维持远程操控的生命周期,防止一旦超出wifi范围,失去心跳后,小车会立即停止。

在这里插入图片描述

在这里插入图片描述

整体的操控效果如下:

在这里插入图片描述

web端配套资源源代码已经上传,下载地址

六、总结

总体来说,在仿真环境下,操控体验还是比较顺畅。主要一些问题是,摄像头的视野较为狭窄,仿真环境中已经使用了120度的广角,一个措施是更改摄像头的安装位置,装的高一些,看到车头和更广的环境,这样有利于远程驾驶。还有就是,可以把画面做得更大一些。

仿真环境vrep的api,sim.py好像长久获取图像不是很稳定,运行一段实际会出现一些内存错误,但不影响学习研究。下篇,我们将此技术用于实际小车的操控。

在这里插入图片描述

七、源码或环境

web端配套资源源代码已经上传(竖屏版),下载地址

仿真配套资源已经上传:下载地址

web端配套资源源代码已经上传(横屏版),下载地址

wifi远程web视频操控机器人小车(python+html

[------------本篇完--------------------------]

PS.扩展阅读

————————————————————————————————————————

对于python机器人编程感兴趣的小伙伴,可以进入如下链接阅读相关咨询

ps1.六自由度机器人相关文章资源

(1) 对六自由度机械臂的运动控制及python实现(附源码)

在这里插入图片描述

(2) N轴机械臂的MDH正向建模,及python算法

在这里插入图片描述

ps2.四轴机器相关文章资源

(1) 文章:python机器人编程——用python实现一个写字机器人

在这里插入图片描述

在这里插入图片描述

(2)python机器人实战——0到1创建一个自动是色块机器人项目-CSDN直播

(3)博文《我从0开始搭建了一个色块自动抓取机器人,并实现了大模型的接入和语音控制-(上基础篇)》的vrep基础环境

(3)博文《我从0开始搭建了一个色块自动抓取机器人,并实现了大模型的接入和语音控制-(上基础篇)》的vrep基础环境

(4)实现了语音输入+大模型指令解析+机器视觉+机械臂流程打通

在这里插入图片描述

在这里插入图片描述

ps3.移动小车相关文章资源

(1)python做了一个极简的栅格地图行走机器人,到底能干啥?[第五弹]——解锁蒙特卡洛定位功能-CSDN博客

(2) 对应python资源:源码地址

在这里插入图片描述

在这里插入图片描述

(3)python机器人编程——差速AGV机器、基于视觉和预测控制的循迹、自动行驶(上篇)_agv编程-CSDN博客

(4)python机器人编程——差速AGV机器、基于视觉和预测控制的循迹、自动行驶(下篇)_agv路线规划原则python-CSDN博客

对应python及仿真环境资源:源码链接

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。