Skip to content

Commit

Permalink
Mt latency (#55)
Browse files Browse the repository at this point in the history
* finx the shortest path

* update layout

* refactor gcode

* delete threejs gcode page

* remove old path algo tests

* adjust delays

* fix index err

* get offset from process table

* move sensor output validation to mt

* fix typo

* find mtct latency

* save mtct latency to process table

* one edge per line

* add actual z value

* get expected z value from db
  • Loading branch information
yuichiroaoki authored Dec 15, 2023
1 parent 9b33bfb commit bb73ff9
Show file tree
Hide file tree
Showing 33 changed files with 750 additions and 575 deletions.
5 changes: 4 additions & 1 deletion server/data/config/dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ mtconnect:
url: https://demo.metalogi.io/current?path=//Components
device: MITSUBISHI1
disable: true
latency_start: 2500
latency_end: 3500
latency_step: 100
sensor:
interval: 10 # ms
threshold: 3.0
beam_diameter: 120.0 # μm
middle_output: 9400
response_time: 15.0 # ms
tolerance: 0.3
tolerance: 0.6
trace:
min_measure_count: 5
max_feedrate: 2500 # mm/min
Expand Down
5 changes: 4 additions & 1 deletion server/data/config/production.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ mtconnect:
url: http://192.168.0.19:5000/current?path=//Components
device: MITSUBISHI1
disable: true
latency_start: 2500
latency_end: 3500
latency_step: 100
sensor:
interval: 10 # ms
threshold: 3.0
beam_diameter: 120 # μm
middle_output: 9400
response_time: 10.0 # ms
tolerance: 0.3
tolerance: 0.6
trace:
min_measure_count: 10
max_feedrate: 2500 # mm/min
Expand Down
20 changes: 19 additions & 1 deletion server/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions server/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mysql-connector-python = "^8.2.0"
pyyaml = "^6.0.1"
trimesh = "^4.0.5"
rtree = "^1.1.0"
networkx = "^3.2.1"


[tool.poetry.group.dev.dependencies]
Expand Down
46 changes: 19 additions & 27 deletions server/server/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from server.type.sensor import SensorConfig
from server.type.mtconnect import MTConnectConfig
from server.type.measurement import (
from server.type import (
MTConnectConfig,
SensorConfig,
GcodeSettings,
EdgeDetectionConfig,
MeasurementConfig,
MeasurementConfigWithProgram,
Expand All @@ -18,8 +19,6 @@
process_stl,
program_number_to_model_id,
)
from pydantic import BaseModel
from typing import Optional
from server import result
from server.listener import (
listener_start,
Expand Down Expand Up @@ -52,17 +51,6 @@
import asyncio


class JobInfo(BaseModel):
three_d_model_id: int
measurement_range: float
measure_feedrate: float
move_feedrate: float
x_offset: Optional[float] = 0.0
y_offset: Optional[float] = 0.0
z_offset: Optional[float] = 0.0
send_gcode: Optional[bool] = True


app = FastAPI()

origins = ["*"]
Expand Down Expand Up @@ -264,25 +252,25 @@ async def load_gcode(model_id: str):


@app.post("/setup/data")
async def setup_data(job_info: JobInfo):
async def setup_data(settings: GcodeSettings):
"""Find verticies, generate gcode"""

filename = model_id_to_filename(job_info.three_d_model_id)
filename = model_id_to_filename(settings.three_d_model_id)
if not model_exists(filename):
raise HTTPException(status_code=400, detail="No model uploaded")
offset = (job_info.x_offset, job_info.y_offset, job_info.z_offset)
offset = (settings.x_offset, settings.y_offset, settings.z_offset)
gcode_settings = (
job_info.measurement_range,
job_info.measure_feedrate,
job_info.move_feedrate,
settings.measurement_range,
settings.measure_feedrate,
settings.move_feedrate,
)
process_stl(
MYSQL_CONFIG,
job_info.three_d_model_id,
settings.three_d_model_id,
filename,
gcode_settings,
offset,
job_info.send_gcode,
settings.send_gcode,
)

return {"status": "ok"}
Expand Down Expand Up @@ -450,8 +438,8 @@ async def websocket_endpoint(websocket: WebSocket):


@app.get("/result/edges/{model_id}")
async def get_result_edges(model_id: int):
edges = result.fetch_edges(model_id)
async def get_result_edges(model_id: int, with_offset: bool = False):
edges = result.fetch_edges(model_id, with_offset)
return {"edges": edges}


Expand Down Expand Up @@ -521,9 +509,13 @@ async def get_result_slopes(model_id: int, process_id: int):


@app.get("/result/mtconnect/lines")
async def get_timestamps_on_lines(model_id: int, process_id: int):
async def get_timestamps_on_lines(
model_id: int, process_id: int, adjusted: bool = True
):
mtct_data_checker = MtctDataChecker(MYSQL_CONFIG, model_id, process_id)
lines = mtct_data_checker.estimate_timestamps_from_mtct_data()
if adjusted:
lines = mtct_data_checker.adjust_delays(lines)
return {"lines": lines.tolist()}


Expand Down
18 changes: 18 additions & 0 deletions server/server/mark/edge.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import mysql.connector
from server.config import MODEL_PATH
import networkx as nx
from .line import get_side
from .arc import get_arc
import math
Expand Down Expand Up @@ -264,7 +265,24 @@ def get_edge_path(
)
arc_edge_path.append(edge_id)
path.append(arc_edge_path)

optimal_path = sorted(path, key=lambda point: (point[2], point[3]))
# calculate the shortest path
G = nx.Graph()
for i, row in enumerate(optimal_path):
G.add_node(i, pos=(row[2], row[3]))
for i in range(len(optimal_path)):
for j in range(i + 1, len(optimal_path)):
weight = (
(optimal_path[i][2] - optimal_path[j][2]) ** 2
+ (optimal_path[i][3] - optimal_path[j][3]) ** 2
) ** 0.5
G.add_edge(i, j, weight=weight)

# Find the optimal path using the Travelling Salesman Problem (TSP)
tour = nx.approximation.traveling_salesman_problem(G, cycle=False)
optimal_path = [optimal_path[node] for node in tour]

if update_data:
self.add_line_number_from_path(optimal_path)
return self.delete_overlap_edges(optimal_path)
Expand Down
56 changes: 21 additions & 35 deletions server/server/measure/estimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
)
from server.mark.edge import (
import_edge_results,
get_edge_id_from_line_number,
delete_edge_results,
)
from server.mark.gcode import get_gcode_filename
Expand All @@ -22,7 +21,7 @@
load_gcode,
get_start_end_points_from_line_number,
)
from .mtconnect import MtctDataChecker
from .mtconnect import MtctDataChecker, update_mtct_latency
from server.mark import arc, pair
from server.mark.trace import (
get_trace_line_id_from_line_number,
Expand All @@ -45,44 +44,22 @@ def __init__(self, mysql_config: dict, model_id: int, process_id: int):
self.conf = get_config()

self.mtct_data_checker = MtctDataChecker(mysql_config, model_id, process_id)
self.mtct_lines = self.mtct_data_checker.estimate_timestamps_from_mtct_data()
mtct_lines = self.mtct_data_checker.estimate_timestamps_from_mtct_data()
self.mtct_lines = self.mtct_data_checker.adjust_delays(mtct_lines)
sensor_data = get_sensor_data(process_id, mysql_config)
self.np_sensor_data = np.array(sensor_data)

model_row = get_model_data(model_id)
filename = model_row[1]
self.stl_filepath = f"{MODEL_PATH}/{filename}"
self.mesh = trimesh.load(self.stl_filepath)
self.offset = (model_row[3], model_row[4], model_row[5])
process_status = status.get_process_status(mysql_config, process_id)
self.offset = (process_status[4], process_status[5], process_status[6])
gcode_filename = get_gcode_filename(filename)
gcode_file_path = f"{GCODE_PATH}/{gcode_filename}"
self.gcode = load_gcode(gcode_file_path)
self.z = self.mtct_data_checker.np_mtconnect_data[0][5]
self.first_line_for_tracing = self.mtct_data_checker.first_line_for_tracing

def get_expected_z_value(self, xy: tuple):
ray_origins = np.array([[xy[0] - self.offset[0], xy[1] - self.offset[1], 100]])
ray_directions = np.array([[0, 0, -1]])
locations = self.mesh.ray.intersects_location(
ray_origins=ray_origins, ray_directions=ray_directions
)[0]
if len(locations) == 0:
return None
# location with the highest z value is the closest point
location = locations[np.argmax(locations[:, 2])]
return location[2] + self.offset[2]

def validate_sensor_output(self, sensor_output: float, start: tuple, end: tuple):
measured_z = sensor_output_to_mm(sensor_output)
edge_xy = (np.array(start) + np.array(end)) / 2
expected_z = self.get_expected_z_value(edge_xy)
# sensor outputs >18800 when there is no workpiece in the sensor range
if expected_z is None:
return sensor_output > 18800
if -35 <= expected_z <= 35:
return abs(measured_z - expected_z) < self.conf["sensor"]["tolerance"]
return sensor_output > 18800

def get_edge_results(self, start_timestamp, end_timestamp, line):
"""
Estimate the exact coordinate of the edge from mtconnect data and
Expand All @@ -96,7 +73,11 @@ def get_edge_results(self, start_timestamp, end_timestamp, line):
sensor_timestamp = sensor_row[2]
sensor_output = sensor_row[3]
if start_timestamp <= sensor_timestamp <= end_timestamp:
if not self.validate_sensor_output(sensor_output, start, end):
(
sensor_output_valid,
edge_ids,
) = self.mtct_data_checker.validate_sensor_output(sensor_output, line)
if not sensor_output_valid:
continue
edge_coord = self.mtct_data_checker.sensor_timestamp_to_coord(
start_timestamp,
Expand All @@ -105,25 +86,29 @@ def get_edge_results(self, start_timestamp, end_timestamp, line):
end,
feedrate,
)
# when edges are overlapping, multiple edges can be measured
# for a single line
edge_ids = get_edge_id_from_line_number(
self.mysql_config, self.model_id, line
)
"""
when edges are overlapping, multiple edges can be measured
for a single line
"""
# edge_ids = get_edge_id_from_line_number(
# self.mysql_config, self.model_id, line
# )

# ignore the rest of the sensor data
# multiple edges can be measured due to the following reasons:
# - noise (can be reduced by increasing the sensor threshold)
# - sensor restart
# - timestamp is not accurate
results = []
measured_z = round(sensor_output_to_mm(sensor_output), 3)
for edge_id in edge_ids:
results.append(
(
edge_id,
self.process_id,
edge_coord[0],
edge_coord[1],
self.z,
measured_z,
)
)
return results
Expand Down Expand Up @@ -264,6 +249,7 @@ def recompute(mysql_config: dict, process_id: int):
arc.delete_measured_arc_info(mysql_config, process_id)
pair.delete_measured_length(mysql_config, process_id)
delete_trace_line_results(mysql_config, process_id)
update_mtct_latency(mysql_config, process_id, None)

process_data = status.get_process_status(mysql_config, process_id)
model_id = process_data[1]
Expand Down
Loading

0 comments on commit bb73ff9

Please sign in to comment.