commit 7b6eec1d2a23a78fe9e4196c327ea5b01a13a162 Author: _Redstone_c_ Date: Mon Aug 9 17:12:24 2021 +0800 初始化提交 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93e6106 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +Binaries +DerivedDataCache +Intermediate +Saved +Build +.vscode +.vs +*.VC.db +*.opensdf +*.opendb +*.sdf +*.sln +*.suo +*.xcodeproj +*.xcworkspace diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini new file mode 100644 index 0000000..e69de29 diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini new file mode 100644 index 0000000..b6d020d --- /dev/null +++ b/Config/DefaultEngine.ini @@ -0,0 +1,24 @@ + + +[/Script/EngineSettings.GameMapsSettings] +GameDefaultMap=/Game/Level.Level +EditorStartupMap=/Game/Level.Level +GlobalDefaultGameMode=/Game/BP_RaspberryVehicleGameMode.BP_RaspberryVehicleGameMode_C + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/RaspberryVehicle") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/RaspberryVehicle") ++ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="RaspberryVehicleGameModeBase") + +[/Script/AndroidRuntimeSettings.AndroidRuntimeSettings] +bPackageDataInsideApk=True + +[/Script/Engine.RendererSettings] +r.DefaultFeature.AntiAliasing=0 + diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini new file mode 100644 index 0000000..6667b31 --- /dev/null +++ b/Config/DefaultGame.ini @@ -0,0 +1,3 @@ + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=ECA46B7040CB59E1C5052F9417760C22 diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini new file mode 100644 index 0000000..655e863 --- /dev/null +++ b/Config/DefaultInput.ini @@ -0,0 +1,5 @@ + + +[/Script/Engine.InputSettings] +DefaultTouchInterface=None + diff --git a/Content/BP_RaspberryVehicleGameMode.uasset b/Content/BP_RaspberryVehicleGameMode.uasset new file mode 100644 index 0000000..9dfe3e7 Binary files /dev/null and b/Content/BP_RaspberryVehicleGameMode.uasset differ diff --git a/Content/Level.umap b/Content/Level.umap new file mode 100644 index 0000000..1770f4c Binary files /dev/null and b/Content/Level.umap differ diff --git a/Content/Level_BuiltData.uasset b/Content/Level_BuiltData.uasset new file mode 100644 index 0000000..f0b5495 Binary files /dev/null and b/Content/Level_BuiltData.uasset differ diff --git a/Content/UMG.uasset b/Content/UMG.uasset new file mode 100644 index 0000000..7ae5bc3 Binary files /dev/null and b/Content/UMG.uasset differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e6a1d95 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +### RaspberryVehicle + +> 这是 2021年山大未来科学营(夏季)- 人工智能与发明创造未来科学营 的作业 + +RaspberryVehicle.py - 树莓派车入口 +RaspberryVehicle.uproject - 控制端入口 + +基于树莓派的简单网络遥控车实现方案。 +支持传感器数据上传,摄像头图像实时上传,远程操作。 +网络协议基于 UDP ,车作为服务端,控制端作为客户端接入。 +客户端基于 Unreal Engine 4 ,其中控制端在 Windows 10 x64 和 安卓 平台上测试通过。 +[查看更多](https://www.myredstone.top/archives/2563) diff --git a/RaspberryVehicle.py b/RaspberryVehicle.py new file mode 100644 index 0000000..ca2a3b6 --- /dev/null +++ b/RaspberryVehicle.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python3 + +import re +import cv2 +import time +import struct +import socket +import _thread +import threading +import RPi.GPIO as GPIO + +stop = False + +interval = 0.1 + +udp_addr = ("192.168.1.1", 6000) + +GPIO.setmode(GPIO.BCM) + +speedl_pin = 18 +speedr_pin = 17 + +GPIO.setup(speedl_pin, GPIO.IN) +GPIO.setup(speedr_pin, GPIO.IN) + +speed_mutex = threading.Lock() +speedl_count = 0 +speedr_count = 0 + +def speedl_callback(pin): + + global speedl_count + global speed_mutex + + speed_mutex.acquire() + speedl_count = speedl_count + 1 + speed_mutex.release() + +def speedr_callback(pin): + + global speedr_count + global speed_mutex + + speed_mutex.acquire() + speedr_count = speedr_count + 1 + speed_mutex.release() + +GPIO.add_event_detect(speedl_pin, GPIO.RISING, callback = speedl_callback) +GPIO.add_event_detect(speedr_pin, GPIO.RISING, callback = speedr_callback) + +ultrasound_tri_pin = 13 +ultrasound_ech_pin = 16 + +GPIO.setup(ultrasound_tri_pin, GPIO.OUT) +GPIO.setup(ultrasound_ech_pin, GPIO.IN) + +ultrasound_distance = 0.0 + +def ultrasound_thread(): + + global stop + + global ultrasound_tri_pin + global ultrasound_ech_pin + global ultrasound_distance + + while not stop: + + GPIO.output(ultrasound_tri_pin, True) + time.sleep(0.00001) + GPIO.output(ultrasound_tri_pin, False) + + count = 10000 + while GPIO.input(ultrasound_ech_pin) != True and count > 0: + count = count - 1 + + start = time.time() + + count = 10000 + while GPIO.input(ultrasound_ech_pin) != False and count > 0: + count = count - 1 + + finish = time.time() + + pulse_len = finish - start + ultrasound_distance = pulse_len / 0.000058 + + time.sleep(interval * 0.01) + +motor_mutex = threading.Lock() +motor_pins = [21, 26, 20, 19] +motor_pwms = [] + +GPIO.setup(motor_pins, GPIO.OUT) + +for motor_index in range(0, 4): + + motor_pwms.append(GPIO.PWM(motor_pins[motor_index], 500)) + motor_pwms[motor_index].start(0) + +motor_mode = 0 +motor_time = 0 +motor_speed = 0 + +def motor_thread(): + + global stop + + global motor_mode + global motor_time + global motor_speed + global motor_mutex + + while not stop: + + motor_mutex.acquire() + + if motor_time < time.time(): + + motor_pwms[0].ChangeDutyCycle(0) + motor_pwms[1].ChangeDutyCycle(0) + motor_pwms[2].ChangeDutyCycle(0) + motor_pwms[3].ChangeDutyCycle(0) + + elif motor_mode == 0: + + motor_pwms[0].ChangeDutyCycle(motor_speed) + motor_pwms[1].ChangeDutyCycle(0) + motor_pwms[2].ChangeDutyCycle(motor_speed) + motor_pwms[3].ChangeDutyCycle(0) + + elif motor_mode == 1: + + motor_pwms[0].ChangeDutyCycle(0) + motor_pwms[1].ChangeDutyCycle(motor_speed) + motor_pwms[2].ChangeDutyCycle(0) + motor_pwms[3].ChangeDutyCycle(motor_speed) + + elif motor_mode == 2: + + motor_pwms[0].ChangeDutyCycle(0) + motor_pwms[1].ChangeDutyCycle(motor_speed * 0.75) + motor_pwms[2].ChangeDutyCycle(motor_speed * 0.75) + motor_pwms[3].ChangeDutyCycle(0) + + elif motor_mode == 3: + + motor_pwms[0].ChangeDutyCycle(motor_speed * 0.75) + motor_pwms[1].ChangeDutyCycle(0) + motor_pwms[2].ChangeDutyCycle(0) + motor_pwms[3].ChangeDutyCycle(motor_speed * 0.75) + + elif motor_mode == 4: + + motor_pwms[0].ChangeDutyCycle(motor_speed) + motor_pwms[1].ChangeDutyCycle(motor_speed) + motor_pwms[2].ChangeDutyCycle(motor_speed) + motor_pwms[3].ChangeDutyCycle(motor_speed) + + motor_mutex.release() + + time.sleep(interval * 0.01) + + +def cap_thread(): + + global stop + + global udp_addr + + cap = cv2.VideoCapture(0) + cap.set(3, 256) + cap.set(4, 256) + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + if cap == None: return + + while not stop: + + for chunk_index in range(0, 256): + + if chunk_index % 32 == 0: + + ret, frame = cap.read() + if ret == False: return + + chunk_bytes = struct.pack("!H", chunk_index) + frame.tobytes()[chunk_index * 768 : chunk_index * 768 + 768] + + sock.sendto(chunk_bytes, udp_addr) + + if stop: return + + cap.release() + +clp_pin = 23 +near_pin = 27 +is_pins = [22, 24, 25, 12, 5] + +GPIO.setup(clp_pin, GPIO.IN, pull_up_down = GPIO.PUD_DOWN) +GPIO.setup(near_pin, GPIO.IN, pull_up_down = GPIO.PUD_DOWN) +GPIO.setup(is_pins, GPIO.IN, pull_up_down = GPIO.PUD_DOWN) + +def udp_thread(): + + global stop + + global udp_addr + + global speedl_count + global speedr_count + global speed_mutex + + global motor_mode + global motor_time + global motor_speed + global motor_mutex + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind(("0.0.0.0", 6000)) + sock.setblocking(False) + + while not stop: + + try: + + (data, source) = sock.recvfrom(1024) + + if len(data) == 8: + + udp_addr = source + + order, param = struct.unpack("!ii", data) + + if order >= 0 and order < 5: + + motor_mutex.acquire() + + motor_mode = order + motor_speed = param + + if motor_speed < 0.0: + + motor_speed = 0.0 + + elif motor_speed > 100.0: + + motor_speed = 100.0 + + motor_time = time.time() + 0.13 + + motor_mutex.release() + + except socket.error: + + motor_mode = 0 + + data = bytes() + + clp_state = GPIO.input(clp_pin) == True + near_state = GPIO.input(near_pin) == True + + data = data + struct.pack("!??", clp_state, near_state) + + for is_pin in is_pins: + + is_state = GPIO.input(is_pin) == True + + data = data + struct.pack("!?", is_state) + + speed_mutex.acquire() + + data = data + struct.pack("!BB", speedl_count, speedr_count) + + speedl_count = 0 + speedr_count = 0 + + speed_mutex.release() + + data = data + struct.pack("!f", ultrasound_distance) + + sock.sendto(data, udp_addr) + + time.sleep(interval) + + sock.close() + +threads = [] + +try: + + threads.append(threading.Thread(target = ultrasound_thread, args = ())) + threads.append(threading.Thread(target = motor_thread, args = ())) + threads.append(threading.Thread(target = cap_thread, args = ())) + threads.append(threading.Thread(target = udp_thread, args = ())) + + for thread in threads: thread.start() + + while True: + + time.sleep(interval) + +except KeyboardInterrupt: + + print("\nKeyboardInterrupt") + +finally: + + stop = True + + for thread in threads: + + if thread.isAlive(): + + thread.join() + + GPIO.cleanup() + + print("Exit") diff --git a/RaspberryVehicle.uproject b/RaspberryVehicle.uproject new file mode 100644 index 0000000..a975f73 --- /dev/null +++ b/RaspberryVehicle.uproject @@ -0,0 +1,13 @@ +{ + "FileVersion": 3, + "EngineAssociation": "4.26", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "RaspberryVehicle", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file diff --git a/Source/RaspberryVehicle.Target.cs b/Source/RaspberryVehicle.Target.cs new file mode 100644 index 0000000..5ac555b --- /dev/null +++ b/Source/RaspberryVehicle.Target.cs @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class RaspberryVehicleTarget : TargetRules +{ + public RaspberryVehicleTarget( TargetInfo Target) : base(Target) + { + Type = TargetType.Game; + DefaultBuildSettings = BuildSettingsVersion.V2; + ExtraModuleNames.AddRange( new string[] { "RaspberryVehicle" } ); + } +} diff --git a/Source/RaspberryVehicle/RaspberryVehicle.Build.cs b/Source/RaspberryVehicle/RaspberryVehicle.Build.cs new file mode 100644 index 0000000..e1cc39a --- /dev/null +++ b/Source/RaspberryVehicle/RaspberryVehicle.Build.cs @@ -0,0 +1,31 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class RaspberryVehicle : ModuleRules +{ + public RaspberryVehicle(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "Engine", + "InputCore" , + "CoreUObject", + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "RHI", + "Sockets", + "Networking", + "RenderCore", + } + ); + } +} diff --git a/Source/RaspberryVehicle/RaspberryVehicle.cpp b/Source/RaspberryVehicle/RaspberryVehicle.cpp new file mode 100644 index 0000000..e4ea47a --- /dev/null +++ b/Source/RaspberryVehicle/RaspberryVehicle.cpp @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "RaspberryVehicle.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, RaspberryVehicle, "RaspberryVehicle" ); diff --git a/Source/RaspberryVehicle/RaspberryVehicle.h b/Source/RaspberryVehicle/RaspberryVehicle.h new file mode 100644 index 0000000..677c8e2 --- /dev/null +++ b/Source/RaspberryVehicle/RaspberryVehicle.h @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + diff --git a/Source/RaspberryVehicle/RaspberryVehicleGameMode.cpp b/Source/RaspberryVehicle/RaspberryVehicleGameMode.cpp new file mode 100644 index 0000000..8506276 --- /dev/null +++ b/Source/RaspberryVehicle/RaspberryVehicleGameMode.cpp @@ -0,0 +1,139 @@ +#include "RaspberryVehicleGameMode.h" + +#include "Sockets.h" +#include "Networking.h" +#include "Misc/Timespan.h" +#include "SocketSubsystem.h" +#include "Engine/Texture2DDynamic.h" + +ARaspberryVehicleGameMode::ARaspberryVehicleGameMode(const FObjectInitializer& ObjectInitializer) +{ + PrimaryActorTick.bCanEverTick = true; +} + +void ARaspberryVehicleGameMode::BeginPlay() +{ + Super::BeginPlay(); + + SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); + + UDPSocket = SocketSubsystem->CreateSocket(NAME_DGram, TEXT("Raspberry Socket"), FNetworkProtocolTypes::IPv4); + + UDPBuffer.SetNumUninitialized(1024); + + CameraBuffer.Init(255, 256 * 256 * 4); + + UDPLastTime = FDateTime(0); + UDPSendTime = FDateTime(0); + + CameraTexture = UTexture2D::CreateTransient(256, 256); + void* Pixels = CameraTexture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(Pixels, CameraBuffer.GetData(), 256 * 256 * 4); + CameraTexture->PlatformData->Mips[0].BulkData.Unlock(); + CameraTexture->UpdateResource(); +} + +void ARaspberryVehicleGameMode::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + int32 BytesNum = 0; + TSharedRef Addr = SocketSubsystem->CreateInternetAddr(); + + bool bIsValid; + Addr->SetIp(TEXT("192.168.1.1"), bIsValid); + Addr->SetIp(*IP, bIsValid); + Addr->SetPort(6000); + + if ((FDateTime::Now() - UDPSendTime) > FTimespan::FromSeconds(0.1)) + { + uint8* MotorModeBuffer = (uint8*)&MotorMode; + + UDPBuffer[0] = MotorModeBuffer[3]; + UDPBuffer[1] = MotorModeBuffer[2]; + UDPBuffer[2] = MotorModeBuffer[1]; + UDPBuffer[3] = MotorModeBuffer[0]; + + uint8* MotorSpeedBuffer = (uint8*)&MotorSpeed; + + UDPBuffer[4] = MotorSpeedBuffer[3]; + UDPBuffer[5] = MotorSpeedBuffer[2]; + UDPBuffer[6] = MotorSpeedBuffer[1]; + UDPBuffer[7] = MotorSpeedBuffer[0]; + + UDPSocket->SendTo(UDPBuffer.GetData(), 8, BytesNum, *Addr); + + UDPSendTime = FDateTime::Now(); + } + + uint32 PendingDataSize; + while(UDPSocket->HasPendingData(PendingDataSize)) + { + UDPSocket->RecvFrom(UDPBuffer.GetData(), UDPBuffer.Num(), BytesNum, *Addr); + + if (BytesNum == 13) + { + UDPLastTime = FDateTime::Now(); + + bCLP = (bool)UDPBuffer[0]; + bNear = (bool)UDPBuffer[1]; + + bIS1 = (bool)UDPBuffer[2]; + bIS2 = (bool)UDPBuffer[3]; + bIS3 = (bool)UDPBuffer[4]; + bIS4 = (bool)UDPBuffer[5]; + bIS5 = (bool)UDPBuffer[6]; + + SpeedL = UDPBuffer[7] * 0.5; + SpeedR = UDPBuffer[8] * 0.5; + + uint8 DistanceBuffer[4]; + + DistanceBuffer[3] = UDPBuffer[9]; + DistanceBuffer[2] = UDPBuffer[10]; + DistanceBuffer[1] = UDPBuffer[11]; + DistanceBuffer[0] = UDPBuffer[12]; + + FMemory::Memcpy(&Distance, DistanceBuffer, sizeof Distance); + } + else if (BytesNum == 770) + { + int32 ChunkIndex; + uint8* ChunkIndexBuffer = (uint8*)&ChunkIndex; + + ChunkIndexBuffer[0] = UDPBuffer[1]; + ChunkIndexBuffer[1] = UDPBuffer[0]; + ChunkIndexBuffer[2] = 0; + ChunkIndexBuffer[3] = 0; + + uint8* Src = &UDPBuffer[2]; + + for (int32 PixelIndex = 0; PixelIndex < 256; ++PixelIndex) + { + CameraBuffer[(ChunkIndex * 256 + PixelIndex) * 4 + 0] = Src[PixelIndex * 3 + 0]; + CameraBuffer[(ChunkIndex * 256 + PixelIndex) * 4 + 1] = Src[PixelIndex * 3 + 1]; + CameraBuffer[(ChunkIndex * 256 + PixelIndex) * 4 + 2] = Src[PixelIndex * 3 + 2]; + CameraBuffer[(ChunkIndex * 256 + PixelIndex) * 4 + 3] = 255; + } + } + } + + void* Buffer = CameraBuffer.GetData(); + FRHITexture2D* RHICameraTexture = CameraTexture->Resource->GetTexture2DRHI(); + ENQUEUE_RENDER_COMMAND(RaspberryCameraUpdate)( + [RHICameraTexture, Buffer](FRHICommandListImmediate& RHICmdList) + { + uint32 Stride = 0; + void* Pixels = RHILockTexture2D(RHICameraTexture, 0, RLM_WriteOnly, Stride, false); + FMemory::Memcpy(Pixels, Buffer, 256 * 256 * 4); + RHIUnlockTexture2D(RHICameraTexture, 0, false); + } + ); +} + +void ARaspberryVehicleGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason) +{ + SocketSubsystem->DestroySocket(UDPSocket); + + Super::EndPlay(EndPlayReason); +} diff --git a/Source/RaspberryVehicle/RaspberryVehicleGameMode.h b/Source/RaspberryVehicle/RaspberryVehicleGameMode.h new file mode 100644 index 0000000..ee4f5f5 --- /dev/null +++ b/Source/RaspberryVehicle/RaspberryVehicleGameMode.h @@ -0,0 +1,82 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Misc/DateTime.h" +#include "GameFramework/GameModeBase.h" +#include "RaspberryVehicleGameMode.generated.h" + +class FSocket; +class UTexture2D; +class ISocketSubsystem; + +UCLASS(Blueprintable) +class ARaspberryVehicleGameMode : public AGameModeBase +{ + GENERATED_BODY() + +public: + + ARaspberryVehicleGameMode(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(BlueprintReadWrite) + FString IP; + + UPROPERTY(BlueprintReadOnly) + FDateTime UDPLastTime; + + UPROPERTY(BlueprintReadOnly) + bool bCLP; + + UPROPERTY(BlueprintReadOnly) + bool bNear; + + UPROPERTY(BlueprintReadOnly) + bool bIS1; + + UPROPERTY(BlueprintReadOnly) + bool bIS2; + + UPROPERTY(BlueprintReadOnly) + bool bIS3; + + UPROPERTY(BlueprintReadOnly) + bool bIS4; + + UPROPERTY(BlueprintReadOnly) + bool bIS5; + + UPROPERTY(BlueprintReadOnly) + float SpeedL = 0.0f; + + UPROPERTY(BlueprintReadOnly) + float SpeedR = 0.0f; + + UPROPERTY(BlueprintReadOnly) + float Distance = 0.0f; + + UPROPERTY(BlueprintReadWrite) + int32 MotorMode = -1; + + UPROPERTY(BlueprintReadWrite) + int32 MotorSpeed = 100; + + UPROPERTY(BlueprintReadWrite) + UTexture2D* CameraTexture; + + ISocketSubsystem* SocketSubsystem; + + FSocket* UDPSocket; + + TArray UDPBuffer; + + TArray CameraBuffer; + + FDateTime UDPSendTime; + + //~ Begin AActor Interface. + virtual void BeginPlay() override; + virtual void Tick(float DeltaSeconds) override; + virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + //~ End AActor Interface. + +}; diff --git a/Source/RaspberryVehicleEditor.Target.cs b/Source/RaspberryVehicleEditor.Target.cs new file mode 100644 index 0000000..b29e2d5 --- /dev/null +++ b/Source/RaspberryVehicleEditor.Target.cs @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class RaspberryVehicleEditorTarget : TargetRules +{ + public RaspberryVehicleEditorTarget( TargetInfo Target) : base(Target) + { + Type = TargetType.Editor; + DefaultBuildSettings = BuildSettingsVersion.V2; + ExtraModuleNames.AddRange( new string[] { "RaspberryVehicle" } ); + } +}