Compare commits
10 Commits
d03acddb67
...
6a7f7a2aff
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a7f7a2aff | ||
|
|
23fc1e8abd | ||
|
|
5c98948af5 | ||
|
|
c2ac30ac2c | ||
|
|
816bd51287 | ||
|
|
051ea83a26 | ||
|
|
5c48abfbd0 | ||
|
|
acda8b9eac | ||
|
|
4c131c103b | ||
|
|
90ad989244 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,4 +5,5 @@ obj/
|
|||||||
node_modules/
|
node_modules/
|
||||||
*.csproj.user
|
*.csproj.user
|
||||||
.idea
|
.idea
|
||||||
/temp/
|
/temp/
|
||||||
|
build/
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
x,y,z
|
|
||||||
143, 1307, 2
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
double degreeY = 0;
|
double degreeY = 0;
|
||||||
String rxData = ""; //Empfangen
|
String rxData = ""; //Empfangen
|
||||||
|
|
||||||
CheapStepper stepper (8,9,10,11);
|
CheapStepper stepper (8,9,10,11);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
@@ -11,7 +10,6 @@ void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
|
||||||
stepper.run();
|
stepper.run();
|
||||||
// send data only when you receive data:
|
// send data only when you receive data:
|
||||||
if (Serial.available() > 0) {
|
if (Serial.available() > 0) {
|
||||||
@@ -37,17 +35,11 @@ void loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String moveMotor(double y){
|
String moveMotor(double y){
|
||||||
|
|
||||||
|
|
||||||
if(y < degreeY){
|
if(y < degreeY){
|
||||||
stepper.newMoveDegrees (true, calculateMove(y)); //true = im Uhrzeigersinn drehen
|
stepper.newMoveDegrees (true, calculateMove(y)); //true = im Uhrzeigersinn drehen
|
||||||
//motor.step((int)calculateStepps(degreeY - y), BACKWARD, INTERLEAVE); //"interleave" means that it alternates between single and double to get twice the resolution (but of course its half the speed)
|
|
||||||
//motor.release(); // Strom sparen und Überhitzung des Controllers vorbeugen!
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
stepper.newMoveDegrees (false, calculateMove(y)); //false = gegen Uhrzeigersinn drehen
|
stepper.newMoveDegrees (false, calculateMove(y)); //false = gegen Uhrzeigersinn drehen
|
||||||
//motor.step((int)calculateStepps(y - degreeY), FORWARD, INTERLEAVE);
|
|
||||||
//motor.release();
|
|
||||||
}
|
}
|
||||||
degreeY = y;
|
degreeY = y;
|
||||||
return "<move><" + (String)y + ">";
|
return "<move><" + (String)y + ">";
|
||||||
|
|||||||
BIN
PointCloudWeb.Scanner/__pycache__/lidar3.cpython-39.pyc
Normal file
BIN
PointCloudWeb.Scanner/__pycache__/lidar3.cpython-39.pyc
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
y, x, z
|
|
||||||
1
PointCloudWeb.Scanner/datafileHigScan40min.txt
Normal file
1
PointCloudWeb.Scanner/datafileHigScan40min.txt
Normal file
File diff suppressed because one or more lines are too long
1
PointCloudWeb.Scanner/datafileMedScan.txt
Normal file
1
PointCloudWeb.Scanner/datafileMedScan.txt
Normal file
File diff suppressed because one or more lines are too long
1
PointCloudWeb.Scanner/datafileMedScan10min.txt
Normal file
1
PointCloudWeb.Scanner/datafileMedScan10min.txt
Normal file
File diff suppressed because one or more lines are too long
1
PointCloudWeb.Scanner/datafileMedScan2.txt
Normal file
1
PointCloudWeb.Scanner/datafileMedScan2.txt
Normal file
File diff suppressed because one or more lines are too long
17640
PointCloudWeb.Scanner/datafileOhneNullDistance.txt
Normal file
17640
PointCloudWeb.Scanner/datafileOhneNullDistance.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
1,0.109375001,1315.000057
|
|
||||||
|
390
PointCloudWeb.Scanner/lidar3.py
Normal file
390
PointCloudWeb.Scanner/lidar3.py
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
from serial import Serial
|
||||||
|
from time import sleep
|
||||||
|
from math import atan,pi,floor
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
name = "PyLidar3"
|
||||||
|
|
||||||
|
class FrequencyStep(Enum):
|
||||||
|
oneTenthHertz=1
|
||||||
|
oneHertz=2
|
||||||
|
|
||||||
|
class YdLidarX4:
|
||||||
|
"""Deals with X4 version of Ydlidar from http://www.ydlidar.com/"""
|
||||||
|
def __init__(self,port,chunk_size=6000,no_value=0):
|
||||||
|
"""Initialize the connection and set port and baudrate."""
|
||||||
|
self._port = port
|
||||||
|
self._baudrate = 128000
|
||||||
|
self._is_scanning = False
|
||||||
|
self._is_connected = False
|
||||||
|
self.chunk_size=chunk_size
|
||||||
|
self._no_value = no_value
|
||||||
|
|
||||||
|
def Connect(self):
|
||||||
|
"""Begin serial connection with Lidar by opening serial port.\nReturn success status True/False.\n"""
|
||||||
|
try:
|
||||||
|
if(not self._is_connected):
|
||||||
|
self._s=Serial(self._port, self._baudrate)
|
||||||
|
self._is_connected = True
|
||||||
|
sleep(3)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
if("YdLidarX4" in str(type(self))):
|
||||||
|
self._Stop_motor()
|
||||||
|
if(self.GetHealthStatus()):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise Exception('Device status error.Try reconnecting device')
|
||||||
|
else:
|
||||||
|
raise Exception("Already connected")
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _Start_motor(self):
|
||||||
|
self._s.setDTR(1)
|
||||||
|
sleep(0.5)
|
||||||
|
|
||||||
|
def _Stop_motor(self):
|
||||||
|
self._s.setDTR(0)
|
||||||
|
sleep(0.5)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _AngleCorr(cls,dist):
|
||||||
|
if dist==0:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return (atan(21.8*((155.3-dist)/(155.3*dist)))*(180/pi))
|
||||||
|
@classmethod
|
||||||
|
def _HexArrToDec(cls,data):
|
||||||
|
littleEndianVal = 0
|
||||||
|
for i in range(0,len(data)):
|
||||||
|
littleEndianVal = littleEndianVal+(data[i]*(256**i))
|
||||||
|
return littleEndianVal
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _Calculate(cls,d):
|
||||||
|
ddict=[]
|
||||||
|
LSN=d[1]
|
||||||
|
Angle_fsa = ((YdLidarX4._HexArrToDec((d[2],d[3]))>>1)/64.0)#+YdLidarX4._AngleCorr(YdLidarX4._HexArrToDec((d[8],d[9]))/4)
|
||||||
|
Angle_lsa = ((YdLidarX4._HexArrToDec((d[4],d[5]))>>1)/64.0)#+YdLidarX4._AngleCorr(YdLidarX4._HexArrToDec((d[LSN+6],d[LSN+7]))/4)
|
||||||
|
if Angle_fsa<Angle_lsa:
|
||||||
|
Angle_diff = Angle_lsa-Angle_fsa
|
||||||
|
else:
|
||||||
|
Angle_diff = 360+Angle_lsa-Angle_fsa
|
||||||
|
for i in range(0,2*LSN,2):
|
||||||
|
# Distance calculation
|
||||||
|
dist_i = YdLidarX4._HexArrToDec((d[8+i],d[8+i+1]))/4
|
||||||
|
# Ignore zero values, they result in massive noise when
|
||||||
|
# computing mean of distances for each angle.
|
||||||
|
if dist_i == 0:
|
||||||
|
continue
|
||||||
|
# Intermediate angle solution
|
||||||
|
Angle_i_tmp = ((Angle_diff/float(LSN-1))*(i/2))+Angle_fsa
|
||||||
|
# Angle correction
|
||||||
|
Angle_i_tmp += YdLidarX4._AngleCorr(dist_i)
|
||||||
|
if Angle_i_tmp > 360:
|
||||||
|
Angle_i = Angle_i_tmp-360
|
||||||
|
elif Angle_i_tmp < 0:
|
||||||
|
Angle_i = Angle_i_tmp+360
|
||||||
|
else:
|
||||||
|
Angle_i = Angle_i_tmp
|
||||||
|
ddict.append((dist_i,Angle_i))
|
||||||
|
return ddict
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _CheckSum(cls,data):
|
||||||
|
try:
|
||||||
|
ocs = YdLidarX4._HexArrToDec((data[6],data[7]))
|
||||||
|
LSN = data[1]
|
||||||
|
cs = 0x55AA^YdLidarX4._HexArrToDec((data[0],data[1]))^YdLidarX4._HexArrToDec((data[2],data[3]))^YdLidarX4._HexArrToDec((data[4],data[5]))
|
||||||
|
for i in range(0,2*LSN,2):
|
||||||
|
cs = cs^YdLidarX4._HexArrToDec((data[8+i],data[8+i+1]))
|
||||||
|
if(cs==ocs):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
@classmethod
|
||||||
|
def _Mean(cls,data):
|
||||||
|
if(len(data)>0):
|
||||||
|
return int(sum(data)/len(data))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def StartScanning(self):
|
||||||
|
"""Begin the lidar and returns a generator which returns a dictionary consisting angle(degrees) and distance(meters).\nReturn Format : {angle(1):distance, angle(2):distance,....................,angle(360):distance}."""
|
||||||
|
if(self._is_connected):
|
||||||
|
if(not self._is_scanning):
|
||||||
|
self._is_scanning = True
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
if("YdLidarX4" in str(type(self))):
|
||||||
|
self._Start_motor()
|
||||||
|
self._s.write(b"\xA5\x60")
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.read(7)
|
||||||
|
distdict = {}
|
||||||
|
countdict = {}
|
||||||
|
lastChunk = None
|
||||||
|
while self._is_scanning == True:
|
||||||
|
for i in range(0,360):
|
||||||
|
distdict.update({i:[]})
|
||||||
|
data = self._s.read(self.chunk_size).split(b"\xaa\x55")
|
||||||
|
if lastChunk is not None:
|
||||||
|
data[0] = lastChunk + data[0]
|
||||||
|
lastChunk = data.pop()
|
||||||
|
for e in data:
|
||||||
|
try:
|
||||||
|
if(e[0]==0):
|
||||||
|
if(YdLidarX4._CheckSum(e)):
|
||||||
|
d = YdLidarX4._Calculate(e)
|
||||||
|
for ele in d:
|
||||||
|
angle = floor(ele[1])
|
||||||
|
if(angle>=0 and angle<360):
|
||||||
|
distdict[angle].append(ele[0])
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
for i in distdict.keys():
|
||||||
|
if len(distdict[i]) > 0:
|
||||||
|
distdict[i]=self._Mean(distdict[i])
|
||||||
|
else:
|
||||||
|
distdict[i]=self._no_value
|
||||||
|
yield distdict
|
||||||
|
else:
|
||||||
|
raise Exception("Device is currently in scanning mode.")
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def StopScanning(self):
|
||||||
|
"""Stops scanning but keeps serial connection alive."""
|
||||||
|
if(self._is_connected):
|
||||||
|
if(self._is_scanning):
|
||||||
|
self._is_scanning = False
|
||||||
|
self._s.write(b"\xA5\x65")
|
||||||
|
sleep(1)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
if("YdLidarX4" in str(type(self))):
|
||||||
|
self._Stop_motor()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not set to scanning mode")
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
|
||||||
|
def GetHealthStatus(self):
|
||||||
|
"""Returns Health status of lidar\nTrue: good\nFalse: Not good"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.write(b"\xA5\x91")
|
||||||
|
sleep(0.5)
|
||||||
|
data = self._s.read(10)
|
||||||
|
if data[9]==0 and data[8]==0 and (data[7]==0 or data[7]==1):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def GetDeviceInfo(self):
|
||||||
|
"""Return device information of lidar in form of dictonary\n{"model_number":model_number,"firmware_version":firmware_version,"hardware_version":hardware_version,"serial_number":serial_number}"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.write(b"\xA5\x90")
|
||||||
|
sleep(0.5)
|
||||||
|
data = self._s.read(27)
|
||||||
|
model_number = str(data[7])
|
||||||
|
firmware_version = str(data[9])+"."+str(data[8])
|
||||||
|
hardware_version = str(data[10])
|
||||||
|
serial_number = ""
|
||||||
|
for i in range(11,20):
|
||||||
|
serial_number = serial_number+str(data[i])
|
||||||
|
return {"model_number":model_number,"firmware_version":firmware_version,"hardware_version":hardware_version,"serial_number":serial_number}
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def Reset(self):
|
||||||
|
"""Reboots the Lidar."""
|
||||||
|
if(self._is_connected):
|
||||||
|
self._s.write(b"\xA5\x80")
|
||||||
|
sleep(0.5)
|
||||||
|
self.Disconnect()
|
||||||
|
self.Connect()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def Disconnect(self):
|
||||||
|
"""Stop scanning and close serial communication with Lidar."""
|
||||||
|
if(self._is_connected):
|
||||||
|
if(self._is_scanning == True):
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.close()
|
||||||
|
self._is_connected=False
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
class YdLidarG4(YdLidarX4):
|
||||||
|
"""Deals with G4 version of Ydlidar from http://www.ydlidar.com/"""
|
||||||
|
def __init__(self,port,chunk_size=6000):
|
||||||
|
"""Initialize the connection and set port and baudrate."""
|
||||||
|
YdLidarX4.__init__(self,port,chunk_size)
|
||||||
|
self._baudrate= 230400
|
||||||
|
|
||||||
|
def EnableLowPowerMode(self):
|
||||||
|
"""Enable Low Power Consumption Mode(Turn motor and distance-measuring unit off in StopScanning)\n"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.write(b"\xA5\x01")
|
||||||
|
while(self._s.inWaiting()==0):
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def DisableLowPowerMode(self):
|
||||||
|
"""Disable Low Power Consumption Mode(Turn motor and distance-measuring unit on StopScanning)\n"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.write(b"\xA5\x02")
|
||||||
|
while(self._s.inWaiting()==0):
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def GetLowPowerModeStatus(self):
|
||||||
|
"""Return True if Low Power Consumption Mode is Enable else return False"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.write(b"\xA5\x05")
|
||||||
|
sleep(1)
|
||||||
|
data = self._s.read(8)
|
||||||
|
if(data[7]!=1):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def IncreaseCurrentFrequency(self,frequencyStep):
|
||||||
|
"""Increase current frequency by oneTenth or one depends on enum FrequencyStep"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
if(frequencyStep.value==1):
|
||||||
|
self._s.write(b"\xA5\x09")
|
||||||
|
elif(frequencyStep.value==2):
|
||||||
|
self._s.write(b"\xA5\x0B")
|
||||||
|
while(self._s.inWaiting()==0):
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def DecreaseCurrentFrequency(self,frequencyStep):
|
||||||
|
"""Decrease current frequency by oneTenth or one depends on enum FrequencyStep"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
if(frequencyStep.value==1):
|
||||||
|
self._s.write(b"\xA5\x0A")
|
||||||
|
elif(frequencyStep.value==2):
|
||||||
|
self._s.write(b"\xA5\x0C")
|
||||||
|
while(self._s.inWaiting()==0):
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def GetCurrentFrequency(self):
|
||||||
|
"""Returns current frequency in hertz"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.write(b"\xA5\x0D")
|
||||||
|
sleep(0.5)
|
||||||
|
data = self._s.read(11)
|
||||||
|
if(data[0]==165):
|
||||||
|
return (self._HexArrToDec(data[-4:]))/100.0
|
||||||
|
else:
|
||||||
|
return (self._HexArrToDec(data[:4]))/100.0
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def EnableConstantFrequency(self):
|
||||||
|
"""Enables constant frequency \n default:Enable"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.write(b"\xA5\x0E")
|
||||||
|
while(self._s.inWaiting()==0):
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def DisableConstantFrequency(self):
|
||||||
|
"""Disable constant frequency \n default:Enable"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.write(b"\xA5\x0F")
|
||||||
|
while(self._s.inWaiting()==0):
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def SwitchRangingFrequency(self):
|
||||||
|
"""Switch between ranging frequencies 4khz, 8khz and 9khz.\ndefault:9khz"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.write(b"\xA5\xD0")
|
||||||
|
while(self._s.inWaiting()==0):
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def GetCurrentRangingFrequency(self):
|
||||||
|
"""Returns current Ranging Frequency in khz"""
|
||||||
|
if(self._is_connected):
|
||||||
|
if self._is_scanning == True:
|
||||||
|
self.StopScanning()
|
||||||
|
self._s.reset_input_buffer()
|
||||||
|
sleep(0.5)
|
||||||
|
self._s.write(b"\xA5\xD1")
|
||||||
|
sleep(1)
|
||||||
|
data = self._s.read(8)
|
||||||
|
if(data[-1]==0):
|
||||||
|
return 4
|
||||||
|
elif(data[-1]==1):
|
||||||
|
return 8
|
||||||
|
elif(data[-1]==2):
|
||||||
|
return 9
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
|
|
||||||
|
def Disconnect(self):
|
||||||
|
"""Stop scanning and close serial communication with Lidar."""
|
||||||
|
if(self._is_connected):
|
||||||
|
if(self._is_scanning == True):
|
||||||
|
self.StopScanning()
|
||||||
|
if(self.GetLowPowerModeStatus()==False):
|
||||||
|
self.EnableLowPowerMode()
|
||||||
|
sleep(2)
|
||||||
|
self._s.close()
|
||||||
|
self._is_connected=False
|
||||||
|
else:
|
||||||
|
raise Exception("Device is not connected")
|
||||||
@@ -9,11 +9,10 @@ import collections
|
|||||||
import requests
|
import requests
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
# f = open("PointCloudWeb.Scanner\datafile.txt","wt")
|
f = open("PointCloudWeb.Scanner\datafile.txt","wt")
|
||||||
# f.write("y, x, z\n")
|
|
||||||
|
|
||||||
arduino_status = False
|
arduino_status = False
|
||||||
arduino_port = "COM9"
|
arduino_port = "COM10"
|
||||||
arduino_baud = 9600
|
arduino_baud = 9600
|
||||||
arduino = None
|
arduino = None
|
||||||
lidar_status = False
|
lidar_status = False
|
||||||
@@ -52,59 +51,61 @@ async def init():
|
|||||||
else:
|
else:
|
||||||
ws_message_queue.appendleft("<connection>false")
|
ws_message_queue.appendleft("<connection>false")
|
||||||
|
|
||||||
def arduino_write_read(x):
|
def setY(y):
|
||||||
arduino.write(bytes(x, 'utf-8'))
|
arduino.write(bytes("<set><"+str(y)+">", 'utf-8'))
|
||||||
data1 = arduino.readline()
|
data1 = arduino.readline()
|
||||||
return filterY(str(data1))
|
return filterY(str(data1))
|
||||||
|
|
||||||
def setY(y):
|
|
||||||
tmp = arduino_write_read("<set><"+str(y)+">")
|
|
||||||
|
|
||||||
def filterY(data):
|
def filterY(data):
|
||||||
temp = data[data.find("<"):data.find(">")]
|
temp = data[data.find("<"):data.find(">")]
|
||||||
return temp + data[data.find("><"):data.find(">", data.find("><")+2)+1]
|
return temp + data[data.find("><"):data.find(">", data.find("><")+2)+1]
|
||||||
|
|
||||||
def senddata(data,posy):
|
def sendData(data,posy):
|
||||||
global scan_id
|
global scan_id
|
||||||
temp ="{\"Id\": \""+scan_id+"\",\"ScanPoints\":["
|
temp ="{\"Id\": \""+scan_id+"\",\"ScanPoints\":["
|
||||||
for x,y in data.items():
|
for x,y in data.items():
|
||||||
temp += ("{\"RAY\":" + str(posy) + ",\"RAX\":" + str(x) + ",\"DistanceMM\":" + str(y) + "},")
|
if y != 0:
|
||||||
# f.write("{\"RAY\":" + str(posy) + ",\"RAX\":" + str(x) + ",\"DistanceMM\":" + str(y) + "},")
|
temp += ("{\"RAY\":" + str(posy) + ",\"RAX\":" + str(x) + ",\"DistanceMM\":" + str(y) + "},")
|
||||||
|
#f.write("{\"RAY\":" + str(posy) + ",\"RAX\":" + str(x) + ",\"DistanceMM\":" + str(y) + "},")
|
||||||
l = len(temp)
|
l = len(temp)
|
||||||
temp = temp[:l-1] + "]}"
|
temp = temp[:l-1] + "]}"
|
||||||
|
f.write(temp)
|
||||||
r = requests.put(url='http://localhost:35588/scandata', data=temp, headers={'content-type': 'application/json'})
|
r = requests.put(url='http://localhost:35588/scandata', data=temp, headers={'content-type': 'application/json'})
|
||||||
#print(r.status_code)
|
#print(r.status_code)
|
||||||
|
|
||||||
|
|
||||||
def startScaner(mode):
|
def startScanner(mode):
|
||||||
global scan_progress, lidar, stop_scan, scan_id
|
global scan_progress, lidar, stop_scan, scan_id, arduino_status
|
||||||
if lidar_status == True:
|
if lidar_status == True and arduino_status == True:
|
||||||
ws_message_queue.appendleft(str(lidar.GetDeviceInfo()))
|
ws_message_queue.appendleft(str(lidar.GetDeviceInfo()))
|
||||||
scan_id = str(uuid.uuid4())
|
scan_id = str(uuid.uuid4())
|
||||||
ws_message_queue.appendleft("Scan ID: " + scan_id)
|
ws_message_queue.appendleft("Scan ID: " + scan_id)
|
||||||
gen = lidar.StartScanning()
|
#gen = lidar.StartScanning()
|
||||||
if mode == "0":
|
if mode == "0":
|
||||||
print("Mode 0")
|
print("Mode 0")
|
||||||
ws_message_queue.appendleft("<scan>running")
|
ws_message_queue.appendleft("<scan>running")
|
||||||
for y in range(1):
|
for y in range(19):
|
||||||
if(stop_scan == True):
|
if(stop_scan == True):
|
||||||
break
|
break
|
||||||
print("send data")
|
gen = lidar.StartScanning()
|
||||||
senddata(next(gen),y*10)
|
sendData(next(gen),y*10)
|
||||||
|
lidar.StopScanning()
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
setY( y*10)
|
setY(y*10)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
scan_progress = round(y/18*100)
|
scan_progress = round(y/18*100)
|
||||||
ws_message_queue.appendleft("<progress>" + str(scan_progress))
|
ws_message_queue.appendleft("<progress>" + str(scan_progress))
|
||||||
r = requests.put(url='http://localhost:35588/scandata/finished/'+scan_id)
|
r = requests.put(url='http://localhost:35588/scandata/finished/'+scan_id)
|
||||||
setY(0)
|
setY(0)
|
||||||
lidar.StopScanning()
|
#lidar.StopScanning()
|
||||||
elif mode == "1":
|
elif mode == "1":
|
||||||
ws_message_queue.appendleft("<scan>running")
|
ws_message_queue.appendleft("<scan>running")
|
||||||
for y in range(91):
|
for y in range(91):
|
||||||
if(stop_scan == True):
|
if(stop_scan == True):
|
||||||
break
|
break
|
||||||
senddata(next(gen),y*2)
|
gen = lidar.StartScanning()
|
||||||
|
sendData(next(gen),y*2)
|
||||||
|
lidar.StopScanning()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
setY(y*2)
|
setY(y*2)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@@ -112,13 +113,15 @@ def startScaner(mode):
|
|||||||
ws_message_queue.appendleft("<progress>" + str(scan_progress))
|
ws_message_queue.appendleft("<progress>" + str(scan_progress))
|
||||||
r = requests.put(url='http://localhost:35588/scandata/finished/'+scan_id)
|
r = requests.put(url='http://localhost:35588/scandata/finished/'+scan_id)
|
||||||
setY(0)
|
setY(0)
|
||||||
lidar.StopScanning()
|
#lidar.StopScanning()
|
||||||
elif mode == "2":
|
elif mode == "2":
|
||||||
ws_message_queue.appendleft("<scan>running")
|
ws_message_queue.appendleft("<scan>running")
|
||||||
for y in range(361):
|
for y in range(361):
|
||||||
if(stop_scan == True):
|
if(stop_scan == True):
|
||||||
break
|
break
|
||||||
senddata(next(gen),y*0.5)
|
gen = lidar.StartScanning()
|
||||||
|
sendData(next(gen),y*0.5)
|
||||||
|
lidar.StopScanning()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
setY(y*0.5)
|
setY(y*0.5)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@@ -126,10 +129,10 @@ def startScaner(mode):
|
|||||||
ws_message_queue.appendleft("<progress>" + str(scan_progress))
|
ws_message_queue.appendleft("<progress>" + str(scan_progress))
|
||||||
r = requests.put(url='http://localhost:35588/scandata/finished/'+scan_id)
|
r = requests.put(url='http://localhost:35588/scandata/finished/'+scan_id)
|
||||||
setY(0)
|
setY(0)
|
||||||
lidar.StopScanning()
|
#lidar.StopScanning()
|
||||||
else:
|
else:
|
||||||
ws_message_queue.appendleft("mode error")
|
ws_message_queue.appendleft("mode error")
|
||||||
#f.close()
|
f.close()
|
||||||
if(stop_scan == True):
|
if(stop_scan == True):
|
||||||
stop_scan = False
|
stop_scan = False
|
||||||
ws_message_queue.appendleft("<scan>canceld")
|
ws_message_queue.appendleft("<scan>canceld")
|
||||||
@@ -150,21 +153,21 @@ async def wsaction(command, value):
|
|||||||
if command == "start":
|
if command == "start":
|
||||||
if value == "0":
|
if value == "0":
|
||||||
ws_message_queue.appendleft("start scan on low resolution")
|
ws_message_queue.appendleft("start scan on low resolution")
|
||||||
x = threading.Thread(target=startScaner, args=(value))
|
x = threading.Thread(target=startScanner, args=(value))
|
||||||
x.start()
|
x.start()
|
||||||
elif value =="1":
|
elif value =="1":
|
||||||
ws_message_queue.appendleft("start scan on medium resolution")
|
ws_message_queue.appendleft("start scan on medium resolution")
|
||||||
x = threading.Thread(target=startScaner, args=(value))
|
x = threading.Thread(target=startScanner, args=(value))
|
||||||
x.start()
|
x.start()
|
||||||
elif value =="2":
|
elif value =="2":
|
||||||
ws_message_queue.appendleft("start scan on high resolution")
|
ws_message_queue.appendleft("start scan on high resolution")
|
||||||
x = threading.Thread(target=startScaner, args=(value))
|
x = threading.Thread(target=startScanner, args=(value))
|
||||||
x.start()
|
x.start()
|
||||||
else:
|
else:
|
||||||
ws_message_queue.appendleft("mode error")
|
ws_message_queue.appendleft("mode error")
|
||||||
elif command == "connect" and arduino and lidar != None:
|
# elif command == "connect" and arduino and lidar != None:
|
||||||
ws_message_queue.appendleft("try to connect to Adruino and LIDAR")
|
# ws_message_queue.appendleft("try to connect to Adruino and LIDAR")
|
||||||
await init()
|
# await init()
|
||||||
elif command == "status":
|
elif command == "status":
|
||||||
ws_message_queue.appendleft("progress: " + scan_progress)
|
ws_message_queue.appendleft("progress: " + scan_progress)
|
||||||
elif command == "stop":
|
elif command == "stop":
|
||||||
|
|||||||
BIN
PointCloudWeb.Scanner/test/__pycache__/lidar3.cpython-39.pyc
Normal file
BIN
PointCloudWeb.Scanner/test/__pycache__/lidar3.cpython-39.pyc
Normal file
Binary file not shown.
@@ -1,21 +1,23 @@
|
|||||||
import PyLidar3
|
import lidar3
|
||||||
import time # Time module
|
import time # Time module
|
||||||
#Serial port to which lidar connected, Get it from device manager windows
|
#Serial port to which lidar connected, Get it from device manager windows
|
||||||
#In linux type in terminal -- ls /dev/tty*
|
#In linux type in terminal -- ls /dev/tty*
|
||||||
#port = input("Enter port name which lidar is connected:") #windows
|
#port = input("Enter port name which lidar is connected:") #windows
|
||||||
#port = "/dev/ttyUSB0" #linux
|
#port = "/dev/ttyUSB0" #linux
|
||||||
f = open("PoinCloudWeb.Scanner\datafile.txt","wt")
|
#f = open("PoinCloudWeb.Scanner\datafile.txt","wt")
|
||||||
Obj = PyLidar3.YdLidarX4(port='COM6',chunk_size=20000) #PyLidar3.your_version_of_lidar(port,chunk_size)
|
Obj = lidar3.YdLidarX4(port='COM6',chunk_size=20000) #PyLidar3.your_version_of_lidar(port,chunk_size)
|
||||||
if(Obj.Connect()):
|
if(Obj.Connect()):
|
||||||
|
# gen = Obj.StartScanning()
|
||||||
|
# t = time.time() # start time
|
||||||
|
# data = next(gen)
|
||||||
|
# #print(data)
|
||||||
|
# for x,y in data.items():
|
||||||
|
# f.write(str(x) + " / " + str(y) + "\n")
|
||||||
|
# f.close()
|
||||||
|
# Obj.StopScanning()
|
||||||
|
Obj.Reset
|
||||||
print(Obj.GetDeviceInfo())
|
print(Obj.GetDeviceInfo())
|
||||||
gen = Obj.StartScanning()
|
print(Obj.GetHealthStatus())
|
||||||
t = time.time() # start time
|
|
||||||
data = next(gen)
|
|
||||||
#print(data)
|
|
||||||
for x,y in data.items():
|
|
||||||
f.write(str(x) + " / " + str(y) + "\n")
|
|
||||||
f.close()
|
|
||||||
Obj.StopScanning()
|
|
||||||
Obj.Disconnect()
|
Obj.Disconnect()
|
||||||
else:
|
else:
|
||||||
print("Error connecting to device")
|
print("Error connecting to device")
|
||||||
@@ -5,6 +5,14 @@
|
|||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PointCloudWeb.Server\PointCloudWeb.Server.csproj" />
|
<ProjectReference Include="..\PointCloudWeb.Server\PointCloudWeb.Server.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -5,6 +5,14 @@
|
|||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace PointCloudWeb.Server
|
namespace PointCloudWeb.Server
|
||||||
{
|
{
|
||||||
@@ -12,12 +13,24 @@ namespace PointCloudWeb.Server
|
|||||||
TempPath = basePath + "/temp";
|
TempPath = basePath + "/temp";
|
||||||
CloudCompareExe = "C:/Program Files/CloudCompare/CloudCompare.exe";
|
CloudCompareExe = "C:/Program Files/CloudCompare/CloudCompare.exe";
|
||||||
LasToolsTxt2Las = basePath + "/PointCloudWeb.Server/Tools/LAStools/txt2las.exe";
|
LasToolsTxt2Las = basePath + "/PointCloudWeb.Server/Tools/LAStools/txt2las.exe";
|
||||||
|
TeaserPp = basePath + "/PointCloudWeb.Server/Tools/TEASERpp/build/teaser_cpp_ply";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LasToolsTxt2Las { get; }
|
|
||||||
|
public static string LasToolsTxt2Las { get; }
|
||||||
public static string PotreeDataPath { get; }
|
public static string PotreeDataPath { get; }
|
||||||
public static string PotreeConverterExe { get; }
|
public static string PotreeConverterExe { get; }
|
||||||
public static string TempPath { get; }
|
public static string TempPath { get; }
|
||||||
public static string CloudCompareExe { get; }
|
public static string CloudCompareExe { get; }
|
||||||
|
public static string TeaserPp { get; }
|
||||||
|
|
||||||
|
public static string ToWslPath(string path)
|
||||||
|
{
|
||||||
|
path = path.Replace("\\", "/");
|
||||||
|
if (path.StartsWith("C:"))
|
||||||
|
return path.Replace("C:", "/mnt/c");
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,12 +63,20 @@ namespace PointCloudWeb.Server.Models
|
|||||||
public string ToStringXyz()
|
public string ToStringXyz()
|
||||||
{
|
{
|
||||||
var stringBuilder = new StringBuilder();
|
var stringBuilder = new StringBuilder();
|
||||||
foreach (var point in Points)
|
|
||||||
|
// var maxPoints = 5_000;
|
||||||
|
|
||||||
|
var takeEvery = 1;//Points.Count / maxPoints;
|
||||||
|
|
||||||
|
for (var i = 0; i < Points.Count; i++)
|
||||||
{
|
{
|
||||||
|
if (i % takeEvery != 0)
|
||||||
|
continue;
|
||||||
|
var point = Points[i];
|
||||||
stringBuilder.AppendLine(string.Join(',',
|
stringBuilder.AppendLine(string.Join(',',
|
||||||
(point.X).ToString(CultureInfo.InvariantCulture),
|
point.X.ToString(CultureInfo.InvariantCulture),
|
||||||
(point.Y).ToString(CultureInfo.InvariantCulture),
|
point.Y.ToString(CultureInfo.InvariantCulture),
|
||||||
(point.Z).ToString(CultureInfo.InvariantCulture)
|
point.Z.ToString(CultureInfo.InvariantCulture)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -76,12 +84,57 @@ namespace PointCloudWeb.Server.Models
|
|||||||
return stringBuilder.ToString();
|
return stringBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ToStringPly(int maxPoints)
|
||||||
|
{
|
||||||
|
var stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.Append("ply\n");
|
||||||
|
stringBuilder.Append("format ascii 1.0\n");
|
||||||
|
|
||||||
|
stringBuilder.Append("element vertex COUNT_PLACEHOLDER\n");
|
||||||
|
stringBuilder.Append("property float x\n");
|
||||||
|
stringBuilder.Append("property float y\n");
|
||||||
|
stringBuilder.Append("property float z\n");
|
||||||
|
stringBuilder.Append("end_header\n");
|
||||||
|
|
||||||
|
if (maxPoints == 0)
|
||||||
|
maxPoints = Points.Count;
|
||||||
|
|
||||||
|
var takeEvery = Points.Count / maxPoints;
|
||||||
|
if (takeEvery < 1)
|
||||||
|
takeEvery = 1;
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < Points.Count; i++)
|
||||||
|
{
|
||||||
|
if (i % takeEvery != 0)
|
||||||
|
continue;
|
||||||
|
count += 1;
|
||||||
|
var point = Points[i];
|
||||||
|
stringBuilder.Append(string.Join(' ',
|
||||||
|
point.X,
|
||||||
|
point.Y,
|
||||||
|
point.Z
|
||||||
|
) + "\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
stringBuilder.Replace("COUNT_PLACEHOLDER", count.ToString());
|
||||||
|
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable once MemberCanBePrivate.Global
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
public void WriteToXyz(string fileName)
|
public void WriteToXyz(string fileName)
|
||||||
{
|
{
|
||||||
File.WriteAllText(fileName, ToStringXyz());
|
File.WriteAllText(fileName, ToStringXyz());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
|
public void WriteToPly(string fileName, int maxPoints)
|
||||||
|
{
|
||||||
|
File.WriteAllText(fileName, ToStringPly(maxPoints));
|
||||||
|
}
|
||||||
|
|
||||||
public void WriteToLasCloudCompare(string fileName)
|
public void WriteToLasCloudCompare(string fileName)
|
||||||
{
|
{
|
||||||
var fileNameXyz = Path.ChangeExtension(fileName, ".xyz");
|
var fileNameXyz = Path.ChangeExtension(fileName, ".xyz");
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -3,8 +3,19 @@ using System.Numerics;
|
|||||||
|
|
||||||
namespace PointCloudWeb.Server.Services
|
namespace PointCloudWeb.Server.Services
|
||||||
{
|
{
|
||||||
public interface IPointCloudRegistationService
|
public class RegistrationResult
|
||||||
{
|
{
|
||||||
public Matrix4x4 RegisterPointCloud(PointCloud source, PointCloud target);
|
public RegistrationResult()
|
||||||
|
{
|
||||||
|
Transformation = Vector3.Zero;
|
||||||
|
Rotation = Vector3.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3 Transformation { get; set; }
|
||||||
|
public Vector3 Rotation { get; set; }
|
||||||
|
}
|
||||||
|
public interface IPointCloudRegistrationService
|
||||||
|
{
|
||||||
|
public RegistrationResult RegisterPointCloud(PointCloud source, PointCloud target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using PointCloudWeb.Server.Models;
|
||||||
|
using PointCloudWeb.Server.Utils;
|
||||||
|
|
||||||
|
// ReSharper disable PossibleMultipleEnumeration
|
||||||
|
|
||||||
|
namespace PointCloudWeb.Server.Services
|
||||||
|
{
|
||||||
|
public class PointCloudRegistrationServiceTeaerPp : IPointCloudRegistrationService
|
||||||
|
{
|
||||||
|
public RegistrationResult RegisterPointCloud(PointCloud source, PointCloud target)
|
||||||
|
{
|
||||||
|
var sourceFileName = Globals.TempPath + $"/{source.Id}.ply";
|
||||||
|
var targetFileName = Globals.TempPath + $"/{target.Id}.ply";
|
||||||
|
|
||||||
|
var outputFileName = Globals.TempPath + $"/{target.Id}.txt";
|
||||||
|
|
||||||
|
var maxPoints = 5_000;
|
||||||
|
|
||||||
|
source.WriteToPly(sourceFileName, maxPoints);
|
||||||
|
target.WriteToPly(targetFileName, maxPoints);
|
||||||
|
|
||||||
|
var teaserPp = new Process();
|
||||||
|
teaserPp.StartInfo.FileName = "wsl";
|
||||||
|
teaserPp.StartInfo.Arguments =
|
||||||
|
$"{Globals.ToWslPath(Globals.TeaserPp)} " +
|
||||||
|
$"\"{Globals.ToWslPath(outputFileName)}\" " +
|
||||||
|
$"\"{Globals.ToWslPath(sourceFileName)}\" " +
|
||||||
|
$"\"{Globals.ToWslPath(targetFileName)}\" ";
|
||||||
|
|
||||||
|
teaserPp.Start();
|
||||||
|
teaserPp.WaitForExit();
|
||||||
|
|
||||||
|
//Console.WriteLine($"RegistrationExitCode: {teaserPp.ExitCode}");
|
||||||
|
|
||||||
|
var result = new RegistrationResult();
|
||||||
|
|
||||||
|
// if (teaserPp.ExitCode != 0)
|
||||||
|
// return result;
|
||||||
|
|
||||||
|
var lines = File.ReadAllLines(outputFileName).ToList();
|
||||||
|
while (lines.Any(s => s.Contains(" ")))
|
||||||
|
lines = lines.Select(s =>
|
||||||
|
{
|
||||||
|
var newString = s.Replace(" ", " ");
|
||||||
|
if (newString.Length > 0 && newString[0] == ' ')
|
||||||
|
newString = newString.Remove(0, 1);
|
||||||
|
return newString;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var rotationIndex = lines.FindIndex(s => s.Contains("Estimated rotation:"));
|
||||||
|
var translationIndex = lines.FindIndex(s => s.Contains("Estimated translation:"));
|
||||||
|
|
||||||
|
var ci = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
var rotationMatrix = Matrix4x4.Identity;
|
||||||
|
rotationMatrix.M11 = float.Parse(lines[rotationIndex + 1].Split(' ')[0], ci);
|
||||||
|
rotationMatrix.M12 = float.Parse(lines[rotationIndex + 1].Split(' ')[1], ci);
|
||||||
|
rotationMatrix.M13 = float.Parse(lines[rotationIndex + 1].Split(' ')[2], ci);
|
||||||
|
rotationMatrix.M21 = float.Parse(lines[rotationIndex + 2].Split(' ')[0], ci);
|
||||||
|
rotationMatrix.M22 = float.Parse(lines[rotationIndex + 2].Split(' ')[1], ci);
|
||||||
|
rotationMatrix.M23 = float.Parse(lines[rotationIndex + 2].Split(' ')[2], ci);
|
||||||
|
rotationMatrix.M31 = float.Parse(lines[rotationIndex + 3].Split(' ')[0], ci);
|
||||||
|
rotationMatrix.M32 = float.Parse(lines[rotationIndex + 3].Split(' ')[1], ci);
|
||||||
|
rotationMatrix.M33 = float.Parse(lines[rotationIndex + 3].Split(' ')[2], ci);
|
||||||
|
|
||||||
|
result.Rotation = MatrixUtils.RotationMatrixToAngles(rotationMatrix);
|
||||||
|
|
||||||
|
var transformation = Vector3.Zero;
|
||||||
|
transformation.X = float.Parse(lines[translationIndex + 1], ci);
|
||||||
|
transformation.Y = float.Parse(lines[translationIndex + 2], ci);
|
||||||
|
transformation.Z = float.Parse(lines[translationIndex + 3], ci);
|
||||||
|
|
||||||
|
result.Transformation = transformation;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Numerics;
|
|
||||||
using PointCloudWeb.Server.Models;
|
using PointCloudWeb.Server.Models;
|
||||||
using PointCloudWeb.Server.Utils;
|
using PointCloudWeb.Server.Utils;
|
||||||
|
|
||||||
@@ -10,13 +9,13 @@ namespace PointCloudWeb.Server.Services
|
|||||||
{
|
{
|
||||||
public class PointCloudService
|
public class PointCloudService
|
||||||
{
|
{
|
||||||
//private readonly IPointCloudRegistrationService pointCloudRegistration;
|
private readonly IPointCloudRegistrationService _pointCloudRegistration;
|
||||||
private readonly PointCloudCollection _pointClouds;
|
private readonly PointCloudCollection _pointClouds;
|
||||||
|
|
||||||
public PointCloudService( /*IPointCloudRegistrationService pointCloudRegistration*/)
|
public PointCloudService( IPointCloudRegistrationService pointCloudRegistration)
|
||||||
{
|
{
|
||||||
_pointClouds = new PointCloudCollection();
|
_pointClouds = new PointCloudCollection();
|
||||||
//this.pointCloudRegistration = pointCloudRegistration;
|
_pointCloudRegistration = pointCloudRegistration;
|
||||||
InitSampleData();
|
InitSampleData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,17 +77,18 @@ namespace PointCloudWeb.Server.Services
|
|||||||
{
|
{
|
||||||
RaiseIfNotExists(id);
|
RaiseIfNotExists(id);
|
||||||
|
|
||||||
var pc = GetById(id);
|
var source = GetById(id);
|
||||||
|
|
||||||
pc.Transformation = Vector3.Zero;
|
//the first can't be registered
|
||||||
pc.Rotation = Vector3.Zero;
|
if (_pointClouds.IndexOf(source) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var target = _pointClouds[_pointClouds.IndexOf(source) - 1];
|
||||||
|
|
||||||
// //the first can't be registered
|
var result = _pointCloudRegistration.RegisterPointCloud(source, target);
|
||||||
// if (_pointClouds.IndexOf(pointCloud) == 0)
|
|
||||||
// return;
|
source.Rotation = result.Rotation;
|
||||||
|
source.Transformation = result.Transformation;
|
||||||
//var transformation = pointCloudRegistration.RegisterPointCloud(pointCloud, pointClouds[0]);
|
|
||||||
//pointCloud.Transformation = transformation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterPointClouds()
|
public void RegisterPointClouds()
|
||||||
@@ -104,8 +104,8 @@ namespace PointCloudWeb.Server.Services
|
|||||||
|
|
||||||
public void PointCloudCompleted(Guid id)
|
public void PointCloudCompleted(Guid id)
|
||||||
{
|
{
|
||||||
RegisterPointCloud(id);
|
|
||||||
GeneratePotreeData(id);
|
GeneratePotreeData(id);
|
||||||
|
RegisterPointCloud(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,13 +7,13 @@ namespace PointCloudWeb.Server.Services
|
|||||||
{
|
{
|
||||||
public static Point Transform(ScanDataPoint scan)
|
public static Point Transform(ScanDataPoint scan)
|
||||||
{
|
{
|
||||||
var alpha = scan.RAX * Math.PI / 180;
|
var alpha = (90 - scan.RAX) * Math.PI / 180;
|
||||||
var beta = scan.RAY * Math.PI / 180;
|
var beta = scan.RAY * Math.PI / 180;
|
||||||
|
|
||||||
var p = new Point(
|
var p = new Point(
|
||||||
y: (int)(scan.DistanceMM * Math.Sin(alpha)),
|
z: (int)(scan.DistanceMM * Math.Sin(alpha)),
|
||||||
x: (int)(scan.DistanceMM * Math.Cos(beta) * Math.Cos(alpha)),
|
x: (int)(scan.DistanceMM * Math.Cos(beta) * Math.Cos(alpha)),
|
||||||
z: (int)(scan.DistanceMM * Math.Sin(beta) * Math.Cos(alpha))
|
y: (int)(scan.DistanceMM * Math.Sin(beta) * Math.Cos(alpha))
|
||||||
);
|
);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ namespace PointCloudWeb.Server
|
|||||||
services.AddSingleton<PointCloudService>();
|
services.AddSingleton<PointCloudService>();
|
||||||
services.AddTransient<ScanConverterService>();
|
services.AddTransient<ScanConverterService>();
|
||||||
services.AddTransient<ScanDataService>();
|
services.AddTransient<ScanDataService>();
|
||||||
|
services.AddTransient<IPointCloudRegistrationService, PointCloudRegistrationServiceTeaerPp>();
|
||||||
services.AddControllers();
|
services.AddControllers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,13 +37,13 @@ namespace PointCloudWeb.Server.Utils
|
|||||||
|
|
||||||
public static void CreateData(PointCloudService pointCloudService)
|
public static void CreateData(PointCloudService pointCloudService)
|
||||||
{
|
{
|
||||||
var pc = pointCloudService.AddPointCloud();
|
// var pc = pointCloudService.AddPointCloud(new Guid("c4b9b7fc-0b97-4f52-ad1b-737aeca5ba97"));
|
||||||
LoadPointCloudFromEthFile(pc, "ETH-Data/Hokuyo_0.csv");
|
// LoadPointCloudFromEthFile(pc, "ETH-Data/Hokuyo_0.csv");
|
||||||
pointCloudService.PointCloudCompleted(pc.Id);
|
// pointCloudService.PointCloudCompleted(pc.Id);
|
||||||
|
//
|
||||||
pc = pointCloudService.AddPointCloud();
|
// pc = pointCloudService.AddPointCloud(new Guid("c620b175-ace8-42e5-bf29-55b6c99372bc"));
|
||||||
LoadPointCloudFromEthFile(pc, "ETH-Data/Hokuyo_1.csv");
|
// LoadPointCloudFromEthFile(pc, "ETH-Data/Hokuyo_1.csv");
|
||||||
pointCloudService.PointCloudCompleted(pc.Id);
|
// pointCloudService.PointCloudCompleted(pc.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace PointCloudWeb.Server.Utils
|
||||||
|
{
|
||||||
|
public class MatrixUtils
|
||||||
|
{
|
||||||
|
public static Vector3 RotationMatrixToAngles(Matrix4x4 m)
|
||||||
|
{
|
||||||
|
var result = Vector3.Zero;
|
||||||
|
|
||||||
|
result.X = (float)Math.Atan2(m.M32, m.M33);
|
||||||
|
result.Y = (float)Math.Atan2(-m.M31, Math.Sqrt(m.M32 * m.M32 + m.M33 * m.M33));
|
||||||
|
result.Z = (float)Math.Atan2(m.M21, m.M11);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
PointCloudWeb.Server/Tools/TEASERpp/CMakeLists.txt
Normal file
19
PointCloudWeb.Server/Tools/TEASERpp/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(teaser_cpp_ply)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
find_package(Eigen3 REQUIRED)
|
||||||
|
find_package(teaserpp REQUIRED)
|
||||||
|
|
||||||
|
# Change this line to include your own executable file
|
||||||
|
add_executable(teaser_cpp_ply teaser_cpp_ply.cc)
|
||||||
|
|
||||||
|
# Link to teaserpp & Eigen3
|
||||||
|
target_link_libraries(teaser_cpp_ply Eigen3::Eigen teaserpp::teaser_registration teaserpp::teaser_io)
|
||||||
|
|
||||||
|
# Copy the data files to build directory
|
||||||
|
file(COPY ../example_data/
|
||||||
|
DESTINATION ./example_data/
|
||||||
|
FILES_MATCHING
|
||||||
|
PATTERN *.ply)
|
||||||
3
PointCloudWeb.Server/Tools/TEASERpp/README.md
Normal file
3
PointCloudWeb.Server/Tools/TEASERpp/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
`bun_zipper.ply` is taken from the [Stanford 3D Scanning Repository](https://graphics.stanford.edu/data/3Dscanrep/).
|
||||||
|
|
||||||
|
In the `3dmatch_sample/` folder, a pair of scan from the `sun3d-home_at-home_at_scan1_2013_jan_1` scene in the 3DMatch dataset is provided. Descriptors calculated by using the `32_dim` pretrained network from [3DSmoothNet](https://github.com/zgojcic/3DSmoothNet) is also provided, together with the ground truth transformation.
|
||||||
101
PointCloudWeb.Server/Tools/TEASERpp/teaser_cpp_ply.cc
Normal file
101
PointCloudWeb.Server/Tools/TEASERpp/teaser_cpp_ply.cc
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// An example showing TEASER++ registration with the Stanford bunny model
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <random>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
|
||||||
|
#include <teaser/ply_io.h>
|
||||||
|
#include <teaser/registration.h>
|
||||||
|
|
||||||
|
// Macro constants for generating noise and outliers
|
||||||
|
#define NOISE_BOUND 0.05
|
||||||
|
// #define N_OUTLIERS 1700
|
||||||
|
// #define OUTLIER_TRANSLATION_LB 5
|
||||||
|
// #define OUTLIER_TRANSLATION_UB 10
|
||||||
|
|
||||||
|
inline double getAngularError(Eigen::Matrix3d R_exp, Eigen::Matrix3d R_est)
|
||||||
|
{
|
||||||
|
return std::abs(std::acos(fmin(fmax(((R_exp.transpose() * R_est).trace() - 1) / 2, -1.0), 1.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
std::ofstream out(argv[1]);
|
||||||
|
std::streambuf *coutbuf = std::cout.rdbuf(); //save old buf
|
||||||
|
std::cout.rdbuf(out.rdbuf()); //redirect std::cout to out.txt!
|
||||||
|
|
||||||
|
std::cout << "You have entered " << argc
|
||||||
|
<< " arguments:"
|
||||||
|
<< "\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < argc; ++i)
|
||||||
|
std::cout << argv[i] << "\n";
|
||||||
|
|
||||||
|
auto src_fileName = argv[2];
|
||||||
|
auto tgt_fileName = argv[3];
|
||||||
|
|
||||||
|
// Load the .ply file
|
||||||
|
teaser::PLYReader reader;
|
||||||
|
teaser::PointCloud src_cloud;
|
||||||
|
auto status = reader.read(src_fileName, src_cloud);
|
||||||
|
int src_cloud_size = src_cloud.size();
|
||||||
|
|
||||||
|
// Convert the point cloud to Eigen
|
||||||
|
Eigen::Matrix<double, 3, Eigen::Dynamic> src(3, src_cloud_size);
|
||||||
|
for (size_t i = 0; i < src_cloud_size; ++i)
|
||||||
|
{
|
||||||
|
src.col(i) << src_cloud[i].x, src_cloud[i].y, src_cloud[i].z;
|
||||||
|
}
|
||||||
|
|
||||||
|
teaser::PointCloud tgt_cloud;
|
||||||
|
status = reader.read(tgt_fileName, tgt_cloud);
|
||||||
|
int tgt_cloud_size = tgt_cloud.size();
|
||||||
|
|
||||||
|
// Convert the point cloud to Eigen
|
||||||
|
Eigen::Matrix<double, 3, Eigen::Dynamic> tgt(3, tgt_cloud_size);
|
||||||
|
for (size_t i = 0; i < tgt_cloud_size; ++i)
|
||||||
|
{
|
||||||
|
tgt.col(i) << tgt_cloud[i].x, tgt_cloud[i].y, tgt_cloud[i].z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run TEASER++ registration
|
||||||
|
// Prepare solver parameters
|
||||||
|
teaser::RobustRegistrationSolver::Params params;
|
||||||
|
params.noise_bound = NOISE_BOUND;
|
||||||
|
params.cbar2 = 1;
|
||||||
|
params.estimate_scaling = false;
|
||||||
|
params.rotation_max_iterations = 100;
|
||||||
|
params.rotation_gnc_factor = 1.4;
|
||||||
|
params.rotation_estimation_algorithm =
|
||||||
|
teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM::GNC_TLS;
|
||||||
|
params.rotation_cost_threshold = 0.005;
|
||||||
|
|
||||||
|
// Solve with TEASER++
|
||||||
|
teaser::RobustRegistrationSolver solver(params);
|
||||||
|
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
|
||||||
|
solver.solve(src, tgt);
|
||||||
|
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
auto solution = solver.getSolution();
|
||||||
|
|
||||||
|
// Compare results
|
||||||
|
std::cout << "=====================================" << std::endl;
|
||||||
|
std::cout << " TEASER++ Results " << std::endl;
|
||||||
|
std::cout << "=====================================" << std::endl;
|
||||||
|
std::cout << "Estimated rotation: " << std::endl;
|
||||||
|
std::cout << solution.rotation << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "Estimated translation: " << std::endl;
|
||||||
|
std::cout << solution.translation << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "Time taken (s): "
|
||||||
|
<< std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() /
|
||||||
|
1000000.0
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
//reset to standard output again, otherwise an error occurs
|
||||||
|
std::cout.rdbuf(coutbuf);
|
||||||
|
}
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!--<button v-on:click="connection_status = !connection_status" >test</button>-->
|
|
||||||
<button v-on:click="progress = 0, logs = []" >clear logs</button>
|
<button v-on:click="progress = 0, logs = []" >clear logs</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -71,7 +70,6 @@ export default {
|
|||||||
if(message.search("<") != -1){
|
if(message.search("<") != -1){
|
||||||
that.command = message.substr(message.search("<")+1, message.search(">")-1)
|
that.command = message.substr(message.search("<")+1, message.search(">")-1)
|
||||||
that.value = message.substr(message.search(">")+1)
|
that.value = message.substr(message.search(">")+1)
|
||||||
//console.log("command: " + that.command + " / value: " + that.value)
|
|
||||||
this.action(that.command, that.value)
|
this.action(that.command, that.value)
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -143,19 +141,17 @@ li {
|
|||||||
.progressbar {
|
.progressbar {
|
||||||
background-color: grey;
|
background-color: grey;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
/* (height of inner div) / 2 + padding */
|
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progressbar>div {
|
.progressbar>div {
|
||||||
/* Adjust with JavaScript */
|
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
background-color: #4CAF50; /* Green */
|
background-color: #4CAF50;
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 15px 32px;
|
padding: 15px 32px;
|
||||||
@@ -173,6 +169,6 @@ li {
|
|||||||
|
|
||||||
.button2 {background-color: #00ba9b;}
|
.button2 {background-color: #00ba9b;}
|
||||||
.button3 {background-color: #008cff}
|
.button3 {background-color: #008cff}
|
||||||
.button4 {background-color: #f44336;} /* Red */
|
.button4 {background-color: #f44336;}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user