|
25 | 25 | - CALIBRATION
|
26 | 26 | - SIMULATION
|
27 | 27 | - CONTROL
|
28 |
| -- Visualizer |
29 | 28 |
|
30 | 29 | """
|
31 | 30 | import os
|
@@ -13484,301 +13483,6 @@ def on_key(event, fig):
|
13484 | 13483 | plt.show()
|
13485 | 13484 |
|
13486 | 13485 |
|
13487 |
| -############################################################################### |
13488 |
| -# Visualizer |
13489 |
| -# class DevServer |
13490 |
| -# class MultiPlot |
13491 |
| -############################################################################### |
13492 |
| - |
13493 |
| -try: |
13494 |
| - import websockets |
13495 |
| -except: |
13496 |
| - pip_install("websockets") |
13497 |
| - |
13498 |
| - |
13499 |
| -class DevServer: |
13500 |
| - """ Dev server """ |
13501 |
| - def __init__(self, loop_fn): |
13502 |
| - self.host = "127.0.0.1" |
13503 |
| - self.port = 5000 |
13504 |
| - self.loop_fn = loop_fn |
13505 |
| - |
13506 |
| - def __del__(self): |
13507 |
| - process = Popen([f"lsof", "-i", ":{self.port}"], stdout=PIPE, stderr=PIPE) |
13508 |
| - stdout, _ = process.communicate() |
13509 |
| - for process in str(stdout.decode("utf-8")).split("\n")[1:]: |
13510 |
| - data = [x for x in process.split(" ") if x != ''] |
13511 |
| - if len(data) <= 1: |
13512 |
| - continue |
13513 |
| - print(f"killing {data[1]}") |
13514 |
| - os.kill(int(data[1]), signal.SIGKILL) |
13515 |
| - |
13516 |
| - def run(self): |
13517 |
| - """ Run server """ |
13518 |
| - kwargs = {"ping_timeout": 1, "close_timeout": 1} |
13519 |
| - server = websockets.serve(self.loop_fn, self.host, self.port, **kwargs) |
13520 |
| - loop = asyncio.get_event_loop() |
13521 |
| - loop.run_until_complete(server) |
13522 |
| - loop.run_forever() |
13523 |
| - |
13524 |
| - @staticmethod |
13525 |
| - def stop(): |
13526 |
| - """ Stop server """ |
13527 |
| - asyncio.get_event_loop().stop() |
13528 |
| - |
13529 |
| - |
13530 |
| -class MultiPlot: |
13531 |
| - """ MultiPlot """ |
13532 |
| - def __init__(self, has_gnd=False): |
13533 |
| - self.plots = [] |
13534 |
| - self.add_pos_xy_plot(has_gnd=has_gnd) |
13535 |
| - self.add_pos_z_plot(has_gnd=has_gnd) |
13536 |
| - self.add_roll_plot(has_gnd=has_gnd) |
13537 |
| - self.add_pitch_plot(has_gnd=has_gnd) |
13538 |
| - self.add_yaw_plot(has_gnd=has_gnd) |
13539 |
| - self.add_pos_error_plot() |
13540 |
| - self.add_att_error_plot() |
13541 |
| - self.add_reproj_error_plot() |
13542 |
| - |
13543 |
| - self.plot_data = {} |
13544 |
| - self.emit_rate = 8.0 # Hz |
13545 |
| - self.last_updated = datetime.now() |
13546 |
| - |
13547 |
| - def _add_plot(self, title, xlabel, ylabel, trace_names, **kwargs): |
13548 |
| - conf = {} |
13549 |
| - conf["title"] = title |
13550 |
| - conf["width"] = kwargs.get("width", 300) |
13551 |
| - conf["height"] = kwargs.get("height", 280) |
13552 |
| - conf["buf_size"] = kwargs.get("buf_size", 100) |
13553 |
| - conf["trace_names"] = trace_names |
13554 |
| - conf["xlabel"] = xlabel |
13555 |
| - conf["ylabel"] = ylabel |
13556 |
| - conf["show_legend"] = (len(trace_names) > 1) |
13557 |
| - self.plots.append(conf) |
13558 |
| - |
13559 |
| - def add_pos_xy_plot(self, **kwargs): |
13560 |
| - """ Add Position X-Y Data """ |
13561 |
| - title = "Position X-Y" |
13562 |
| - xlabel = "x [m]" |
13563 |
| - ylabel = "y [m]" |
13564 |
| - trace_names = ["Estimate"] |
13565 |
| - if kwargs.get("has_gnd"): |
13566 |
| - trace_names.append("Ground-Truth") |
13567 |
| - |
13568 |
| - self._add_plot(title, xlabel, ylabel, trace_names) |
13569 |
| - |
13570 |
| - def add_pos_z_plot(self, **kwargs): |
13571 |
| - """ Add Position Z Data """ |
13572 |
| - xlabel = "Time [s]" |
13573 |
| - ylabel = "y [m]" |
13574 |
| - trace_names = ["Estimate"] |
13575 |
| - if kwargs.get("has_gnd"): |
13576 |
| - trace_names.append("Ground-Truth") |
13577 |
| - |
13578 |
| - self._add_plot("Position Z", xlabel, ylabel, trace_names) |
13579 |
| - |
13580 |
| - def add_roll_plot(self, **kwargs): |
13581 |
| - """ Add Roll Data """ |
13582 |
| - xlabel = "Time [s]" |
13583 |
| - ylabel = "Attitude [deg]" |
13584 |
| - trace_names = ["Estimate"] |
13585 |
| - if kwargs.get("has_gnd"): |
13586 |
| - trace_names.append("Ground-Truth") |
13587 |
| - |
13588 |
| - self._add_plot("Roll", xlabel, ylabel, trace_names) |
13589 |
| - |
13590 |
| - def add_pitch_plot(self, **kwargs): |
13591 |
| - """ Add Roll Data """ |
13592 |
| - xlabel = "Time [s]" |
13593 |
| - ylabel = "Attitude [deg]" |
13594 |
| - trace_names = ["Estimate"] |
13595 |
| - if kwargs.get("has_gnd"): |
13596 |
| - trace_names.append("Ground-Truth") |
13597 |
| - |
13598 |
| - self._add_plot("Pitch", xlabel, ylabel, trace_names) |
13599 |
| - |
13600 |
| - def add_yaw_plot(self, **kwargs): |
13601 |
| - """ Add Yaw Data """ |
13602 |
| - xlabel = "Time [s]" |
13603 |
| - ylabel = "Attitude [deg]" |
13604 |
| - trace_names = ["Estimate"] |
13605 |
| - if kwargs.get("has_gnd"): |
13606 |
| - trace_names.append("Ground-Truth") |
13607 |
| - |
13608 |
| - self._add_plot("Yaw", xlabel, ylabel, trace_names) |
13609 |
| - |
13610 |
| - def add_pos_error_plot(self): |
13611 |
| - """ Add Position Error Data """ |
13612 |
| - title = "Position Error" |
13613 |
| - xlabel = "Time [s]" |
13614 |
| - ylabel = "Position Error [m]" |
13615 |
| - trace_names = ["Error"] |
13616 |
| - self._add_plot(title, xlabel, ylabel, trace_names) |
13617 |
| - |
13618 |
| - def add_att_error_plot(self): |
13619 |
| - """ Add Attitude Error Data """ |
13620 |
| - title = "Attitude Error" |
13621 |
| - xlabel = "Time [s]" |
13622 |
| - ylabel = "Position Error [m]" |
13623 |
| - trace_names = ["Error"] |
13624 |
| - self._add_plot(title, xlabel, ylabel, trace_names) |
13625 |
| - |
13626 |
| - def add_reproj_error_plot(self): |
13627 |
| - """ Add Reprojection Error Data """ |
13628 |
| - title = "Reprojection Error" |
13629 |
| - xlabel = "Time [s]" |
13630 |
| - ylabel = "Reprojection Error [px]" |
13631 |
| - trace_names = ["Mean", "RMSE"] |
13632 |
| - self._add_plot(title, xlabel, ylabel, trace_names) |
13633 |
| - |
13634 |
| - def _form_plot_data(self, plot_title, time_s, **kwargs): |
13635 |
| - gnd = kwargs.get("gnd") |
13636 |
| - est = kwargs.get("est") |
13637 |
| - err = kwargs.get("err") |
13638 |
| - |
13639 |
| - conf = {plot_title: {}} |
13640 |
| - if gnd: |
13641 |
| - conf[plot_title]["Ground-Truth"] = {"x": time_s, "y": gnd} |
13642 |
| - |
13643 |
| - if est: |
13644 |
| - conf[plot_title]["Estimate"] = {"x": time_s, "y": est} |
13645 |
| - |
13646 |
| - if err: |
13647 |
| - conf[plot_title]["Error"] = {"x": time_s, "y": err} |
13648 |
| - |
13649 |
| - self.plot_data.update(conf) |
13650 |
| - |
13651 |
| - def add_pos_xy_data(self, **kwargs): |
13652 |
| - """ Add Position X-Y Data """ |
13653 |
| - plot_title = "Position X-Y" |
13654 |
| - conf = {plot_title: {}} |
13655 |
| - |
13656 |
| - if "gnd" in kwargs: |
13657 |
| - gnd = kwargs["gnd"] |
13658 |
| - conf[plot_title]["Ground-Truth"] = {"x": gnd[0], "y": gnd[1]} |
13659 |
| - |
13660 |
| - if "est" in kwargs: |
13661 |
| - est = kwargs["est"] |
13662 |
| - conf[plot_title]["Estimate"] = {"x": est[0], "y": est[1]} |
13663 |
| - |
13664 |
| - self.plot_data.update(conf) |
13665 |
| - |
13666 |
| - def add_pos_z_data(self, time_s, **kwargs): |
13667 |
| - """ Add Position Z Data """ |
13668 |
| - self._form_plot_data("Position Z", time_s, **kwargs) |
13669 |
| - |
13670 |
| - def add_roll_data(self, time_s, **kwargs): |
13671 |
| - """ Add Roll Data """ |
13672 |
| - self._form_plot_data("Roll", time_s, **kwargs) |
13673 |
| - |
13674 |
| - def add_pitch_data(self, time_s, **kwargs): |
13675 |
| - """ Add Roll Data """ |
13676 |
| - self._form_plot_data("Pitch", time_s, **kwargs) |
13677 |
| - |
13678 |
| - def add_yaw_data(self, time_s, **kwargs): |
13679 |
| - """ Add Yaw Data """ |
13680 |
| - self._form_plot_data("Yaw", time_s, **kwargs) |
13681 |
| - |
13682 |
| - def add_pos_error_data(self, time_s, error): |
13683 |
| - """ Add Position Error Data """ |
13684 |
| - self._form_plot_data("Position Error", time_s, err=error) |
13685 |
| - |
13686 |
| - def add_att_error_data(self, time_s, error): |
13687 |
| - """ Add Attitude Error Data """ |
13688 |
| - self._form_plot_data("Attitude Error", time_s, err=error) |
13689 |
| - |
13690 |
| - def add_reproj_error_data(self, time_s, reproj_rmse, reproj_mean): |
13691 |
| - """ Add Reprojection Error Data """ |
13692 |
| - plot_title = "Reprojection Error" |
13693 |
| - conf = {plot_title: {}} |
13694 |
| - conf[plot_title]["Mean"] = {"x": time_s, "y": reproj_rmse} |
13695 |
| - conf[plot_title]["RMSE"] = {"x": time_s, "y": reproj_mean} |
13696 |
| - self.plot_data.update(conf) |
13697 |
| - |
13698 |
| - def get_plots(self): |
13699 |
| - """ Get plots """ |
13700 |
| - return json.dumps(self.plots) |
13701 |
| - |
13702 |
| - def get_plot_data(self): |
13703 |
| - """ Get plot data """ |
13704 |
| - return json.dumps(self.plot_data) |
13705 |
| - |
13706 |
| - async def emit_data(self, ws): |
13707 |
| - """ Emit data """ |
13708 |
| - time_now = datetime.now() |
13709 |
| - time_diff = (time_now - self.last_updated).total_seconds() |
13710 |
| - if time_diff > (1.0 / self.emit_rate): |
13711 |
| - await ws.send(self.get_plot_data()) |
13712 |
| - self.last_updated = time_now |
13713 |
| - |
13714 |
| - |
13715 |
| -async def fake_loop(ws, _): |
13716 |
| - """ Simulates a simulation or dev loop """ |
13717 |
| - # Setup plots |
13718 |
| - print("Connected to client!") |
13719 |
| - multi_plot = MultiPlot(has_gnd=True) |
13720 |
| - await ws.send(multi_plot.get_plots()) |
13721 |
| - |
13722 |
| - # Loop |
13723 |
| - index = 0 |
13724 |
| - |
13725 |
| - while True: |
13726 |
| - index += 1 |
13727 |
| - |
13728 |
| - t = index |
13729 |
| - x = np.random.random() |
13730 |
| - y = np.random.random() |
13731 |
| - z = np.random.random() |
13732 |
| - gnd = np.random.random(3) |
13733 |
| - est = np.random.random(3) |
13734 |
| - multi_plot.add_pos_xy_data(est=est, gnd=gnd) |
13735 |
| - multi_plot.add_pos_z_data(t, est=z, gnd=x) |
13736 |
| - multi_plot.add_roll_data(t, est=x, gnd=y) |
13737 |
| - multi_plot.add_pitch_data(t, est=x, gnd=y) |
13738 |
| - multi_plot.add_yaw_data(t, est=x, gnd=y) |
13739 |
| - multi_plot.add_pos_error_data(t, y) |
13740 |
| - multi_plot.add_att_error_data(t, x) |
13741 |
| - multi_plot.add_reproj_error_data(t, x, y) |
13742 |
| - await multi_plot.emit_data(ws) |
13743 |
| - |
13744 |
| - # Important |
13745 |
| - await ws.close() |
13746 |
| - DevServer.stop() |
13747 |
| - |
13748 |
| - |
13749 |
| -class TestViz(unittest.TestCase): |
13750 |
| - """ Test Viz """ |
13751 |
| - def test_multiplot(self): |
13752 |
| - """ Test MultiPlot() """ |
13753 |
| - t = 0 |
13754 |
| - x = np.random.random() |
13755 |
| - y = np.random.random() |
13756 |
| - z = np.random.random() |
13757 |
| - gnd = np.random.random(3) |
13758 |
| - est = np.random.random(3) |
13759 |
| - multi_plot = MultiPlot(has_gnd=True) |
13760 |
| - multi_plot.add_pos_xy_data(est=est, gnd=gnd) |
13761 |
| - multi_plot.add_pos_z_data(t, est=z, gnd=x) |
13762 |
| - multi_plot.add_roll_data(t, est=x, gnd=y) |
13763 |
| - multi_plot.add_pitch_data(t, est=x, gnd=y) |
13764 |
| - multi_plot.add_yaw_data(t, est=x, gnd=y) |
13765 |
| - multi_plot.add_pos_error_data(t, y) |
13766 |
| - multi_plot.add_att_error_data(t, x) |
13767 |
| - multi_plot.add_reproj_error_data(t, x, y) |
13768 |
| - |
13769 |
| - # import pprint |
13770 |
| - # pprint.pprint(multi_plot.get_plots()) |
13771 |
| - |
13772 |
| - self.assertTrue(multi_plot is not None) |
13773 |
| - |
13774 |
| - @unittest.skip("") |
13775 |
| - def test_server(self): |
13776 |
| - """ Test DevServer() """ |
13777 |
| - viz_server = DevServer(fake_loop) |
13778 |
| - viz_server.run() |
13779 |
| - self.assertTrue(viz_server is not None) |
13780 |
| - |
13781 |
| - |
13782 | 13486 | ###############################################################################
|
13783 | 13487 | # Sandbox
|
13784 | 13488 | ###############################################################################
|
|
0 commit comments