TkLRFViewer.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # -*- coding: utf-8 -*-
00003 # -*- Python -*-
00004 #
00005 # @brief LRFViewer component
00006 # @date $Date$
00007 # @author Norkai Ando <n-ando@aist.go.jp>
00008 #
00009 # Copyright (C) 2008
00010 #     Noriaki Ando
00011 #     Task-intelligence Research Group,
00012 #     Intelligent Systems Research Institute,
00013 #     National Institute of
00014 #         Advanced Industrial Science and Technology (AIST), Japan
00015 #     All rights reserved.
00016 #
00017 # $Id$
00018 #
00019 
00020 # $Log$
00021 #
00022 
00023 from Tix import *
00024 import time
00025 import math
00026 
00027 # Import RTM module
00028 import RTC
00029 import OpenRTM_aist
00030 # This module's spesification
00031 # <rtc-template block="module_spec">
00032 lrfviewer_spec = ["implementation_id",     "LRFViewer", 
00033                   "type_name",         "LRFViewer", 
00034                   "description",       "Laser Range Finder Viewer component", 
00035                   "version",           "1.0", 
00036                   "vendor",            "Noriaki Ando, AIST", 
00037                   "category",          "example", 
00038                   "activity_type",     "DataFlowComponent", 
00039                   "max_instance",      "1", 
00040                   "language",          "Python", 
00041                   "lang_type",         "SCRIPT",
00042                   ""]
00043 # </rtc-template>
00044 
00045 
00046 #------------------------------------------------------------
00047 # LRFViewer component
00048 #
00049 #------------------------------------------------------------
00050 class LRFViewer(OpenRTM_aist.DataFlowComponentBase):
00051   def __init__(self, manager):
00052     OpenRTM_aist.DataFlowComponentBase.__init__(self, manager)
00053 
00054     self.range_data = []
00055     self.start_point = 0
00056     self.end_point   = 768
00057     self.angular_res = 0.0
00058     return
00059 
00060   def onInitialize(self):
00061     _pose3D = RTC.Pose3D(RTC.Point3D(0.0, 0.0, 0.0),
00062                          RTC.Orientation3D(0.0, 0.0, 0.0))
00063     _size3D = RTC.Size3D(0.0, 0.0, 0.0)
00064     _geometry3D = RTC.Geometry3D(_pose3D, _size3D)
00065     _rangerConfig = RTC.RangerConfig(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
00066     self._d_rangeData = RTC.RangeData(RTC.Time(0,0),
00067                                       [],
00068                                       RTC.RangerGeometry(_geometry3D, []),
00069                                       _rangerConfig)
00070 
00071     self._rangeDataIn = OpenRTM_aist.InPort("range_data", self._d_rangeData)
00072     self.addInPort("range_data",  self._rangeDataIn)
00073 
00074     return RTC.RTC_OK
00075 
00076   def onShutdown(self, ec_id):
00077     return RTC.RTC_OK
00078 
00079   def onDeactivated(self, ec_id):
00080     self.range_data = []
00081     self.start_point = 0
00082     self.end_point = 768
00083     return RTC.RTC_OK
00084 
00085   def onExecute(self, ec_id):
00086     if self._rangeDataIn.isNew():
00087       _rangeData = self._rangeDataIn.read()
00088       self.range_data = _rangeData.ranges
00089       self.start_point = _rangeData.config.minAngle
00090       self.end_point = _rangeData.config.maxAngle
00091       self.angular_res = _rangeData.config.angularRes
00092     time.sleep(0.01)
00093     return RTC.RTC_OK
00094 
00095   def get_range_data(self):
00096     return self.range_data
00097 
00098   def get_start_point(self):
00099     return self.start_point
00100 
00101   def get_end_point(self):
00102     return self.end_point
00103 
00104   def get_angular_res(self):
00105     return self.angular_res
00106 
00107 
00108 class ToggleItem:
00109   def __init__(self):
00110     self.active = True
00111     return
00112 
00113   def __del__(self):
00114     self.delete()
00115     return
00116 
00117   def activate(self):
00118     self.active = True
00119     self.draw()
00120     return
00121 
00122   def deactivate(self):
00123     self.active = False
00124     self.delete()
00125     return
00126 
00127   def toggle(self):
00128     if self.active:
00129       self.deactivate()
00130     else:
00131       self.activate()
00132     return
00133 
00134 
00135 class CanvasText(ToggleItem):
00136   def __init__(self, canvas, text, x, y):
00137     ToggleItem.__init__(self)
00138     self.canvas = canvas
00139     self.id = self.canvas.create_text(x, y, text=text)
00140     self.text = text
00141     self.x = x
00142     self.y = y
00143     self.draw_text(x, y, text)
00144     return
00145 
00146   def draw(self):
00147     if self.active == False: return
00148     self.delete()
00149     self.id = self.canvas.create_text(self.x, self.y, text=self.text)
00150     return
00151 
00152   def draw_text(self, x, y, text):
00153     self.x = x
00154     self.y = y
00155     self.text = text
00156     self.draw()
00157     return
00158 
00159   def delete(self):
00160     self.canvas.delete(self.id)
00161     return
00162 
00163 class CanvasGrid(ToggleItem):
00164   def __init__(self, canvas, x0, y0, width, height, pitch, color, linewd):
00165     ToggleItem.__init__(self)
00166     self.canvas = canvas
00167     self.x0 = x0
00168     self.y0 = y0
00169     self.width = width
00170     self.height = height
00171     self.pitch = pitch
00172     self.color = color
00173     self.linewd = linewd
00174     self.idx = []
00175     self.idy = []
00176 
00177     self.draw()
00178     return
00179 
00180   def draw(self):
00181     if self.active == False: return
00182     self.delete()
00183     
00184     x_start = int(self.x0 % self.pitch)
00185     x_num   = int((self.width - x_start) / self.pitch) + 1
00186     for x in range(x_num):
00187       x0 = x_start + self.pitch * x
00188       id = self.canvas.create_line(x0, 0, x0, self.height,
00189                                    fill=self.color, width=self.linewd)
00190       self.idx.append(id)
00191 
00192     y_start = int(self.y0 % self.pitch)
00193     y_num   = int((self.height - y_start) / self.pitch) + 1
00194     for y in range(y_num):
00195       y0 = y_start + self.pitch * y
00196       id = self.canvas.create_line(0, y0, self.width, y0,
00197                                    fill=self.color, width=self.linewd)
00198       self.idy.append(id)
00199 
00200     for i in self.idx:
00201       self.canvas.tag_lower(i)
00202     for i in self.idy:
00203       self.canvas.tag_lower(i)
00204 
00205     return
00206 
00207   def delete(self):
00208     for i in self.idx:
00209       self.canvas.delete(i)
00210     for i in self.idy:
00211       self.canvas.delete(i)
00212     return
00213 
00214   def set_pitch(self, pitch):
00215     if pitch != 0:
00216       self.pitch = pitch
00217 
00218     self.draw()
00219     return
00220 
00221 
00222 class CanvasAxis(ToggleItem):
00223   def __init__(self, canvas, width, height, color="#ffffff", linewd=1):
00224     ToggleItem.__init__(self)
00225     self.x0 = width/2
00226     self.y0 = height/2
00227     self.width = width
00228     self.height = height
00229     self.color = color
00230     self.linewd = linewd
00231     self.canvas = canvas
00232     self.id = [None] * 4
00233     self.draw()
00234     return
00235 
00236   def draw(self):
00237     if self.active == False: return
00238     self.delete()
00239     self.id[0] = self.canvas.create_line(0, self.height/2, 
00240                                          self.width, self.height/2,
00241                                          fill = self.color,
00242                                          width = self.linewd)
00243     self.id[1] = self.canvas.create_text(self.width - 10,
00244                                          self.height/2 + 10,
00245                                          text="x",
00246                                          fill = self.color,
00247                                          font="courier 12")
00248     self.id[2] = self.canvas.create_line(self.width/2, 0, 
00249                                          self.width/2, self.height,
00250                                          fill = self.color,
00251                                          width = self.linewd)
00252     self.id[3] = self.canvas.create_text(self.width/2 + 10,
00253                                          + 10, text="y",
00254                                          fill = self.color,
00255                                          font="courier 12")
00256         
00257     return
00258 
00259   def delete(self):
00260     for i in self.id:
00261       self.canvas.delete(i)
00262     return
00263 
00264 
00265 class ScaledObject:
00266   def __init__(self, simulator):
00267     self.simulator = simulator
00268     self.tick = simulator.get_tick()
00269     self.canvas = simulator.get_canvas()
00270     self.trans = simulator.get_translation()
00271     return
00272 
00273   def translate(self, x, y, dx, dy, dth):
00274     return self.trans(x, y, dx, dy, dth)
00275 
00276   def get_tick(self):
00277     return self.simulator.get_tick()
00278 
00279 
00280 #------------------------------------------------------------
00281 # LRFrange range data drawing
00282 #
00283 #------------------------------------------------------------
00284 class LRFrange(ScaledObject):
00285   def __init__(self, simulator,
00286                line_color="#ff0000", fill_color="#ff0000", linewd=1):
00287     ScaledObject.__init__(self, simulator)
00288     self.fill_color = fill_color
00289     self.line_color = line_color
00290     self.default_fill_color = fill_color
00291     self.default_line_color = line_color
00292     self.linewd = linewd
00293     self.rdata = []
00294     self.pre_data = []
00295     self.poly_id = None
00296     self.source = None
00297 
00298     # URG parameter
00299     self.beg_angle = -45
00300     self.end_angle = 225
00301     self.angle_per_step = 360.0 / 1024.0
00302     self.valid_beg_angle = 44 * 360.0 / 1024.0
00303     self.valid_end_angle = self.valid_beg_angle + 725 * self.angle_per_step
00304     self.offset_step = 0
00305 
00306     self.threshold = 0.0
00307     self.sfilter = 0.0
00308     self.tfilter = 0.0
00309 
00310     self.threshold_check = BooleanVar()
00311     self.threshold_check.set(True)
00312     self.tfilter_check = BooleanVar()
00313     self.tfilter_check.set(True)
00314     self.sfilter_check = BooleanVar()
00315     self.sfilter_check.set(True)
00316 
00317     self.threshold_var = DoubleVar()
00318     self.threshold_var.set(self.threshold)
00319     self.tfilter_var = DoubleVar()
00320     self.tfilter_var.set(self.tfilter)
00321     self.sfilter_var = DoubleVar()
00322     self.sfilter_var.set(self.sfilter)
00323 
00324     self.update()
00325     return
00326 
00327   def create_ctrl(self, frame):
00328     self.lrf_fill_check = StringVar()
00329     self.lrf_fill_check.set("on")
00330     self.lrf_line_check = StringVar()
00331     self.lrf_line_check.set("on")
00332 
00333     text = Label(frame, text="LRF range area", anchor=W, justify=LEFT)
00334     # "Line" check box
00335     line = Checkbutton(frame, text="Line",
00336                        onvalue="on", offvalue="off",
00337                        justify=LEFT, anchor=W,
00338                        variable=self.lrf_line_check,
00339                        command=self.line_toggle)
00340     # "Fill" check box
00341     fill = Checkbutton(frame, text="Fill",
00342                        onvalue="on", offvalue="off",
00343                        justify=LEFT, anchor=W,
00344                        variable=self.lrf_fill_check,
00345                        command=self.fill_toggle)
00346     # Threshold (check box/scale)
00347     thresh = Checkbutton(frame, text="Threshold",
00348                          onvalue=True, offvalue=False,
00349                          justify=LEFT, anchor=W,
00350                          variable=self.threshold_check)
00351     thresh_scale = Scale(frame, from_=0, to=100, resolution=0.1,
00352                          label="Threshold", command=self.on_threshold,
00353                          variable=self.threshold_var, orient=HORIZONTAL)
00354     # Time-Filter (check box/scale)
00355     tfilter = Checkbutton(frame, text="Filter(Time)",
00356                           onvalue=True, offvalue=False,
00357                           justify=LEFT, anchor=W,
00358                           variable=self.tfilter_check)
00359     tfilter_scale = Scale(frame, from_=0, to=1, resolution=0.01,
00360                           label="Filter", command=self.on_tfilter,
00361                           variable=self.tfilter_var, orient=HORIZONTAL)
00362     # Spacial-Filter (check box/scale)
00363     sfilter = Checkbutton(frame, text="Filter(Spacial)",
00364                           onvalue=True, offvalue=False,
00365                           justify=LEFT, anchor=W,
00366                           variable=self.sfilter_check)
00367     sfilter_scale = Scale(frame, from_=0, to=1, resolution=0.01,
00368                           label="Filter", command=self.on_sfilter,
00369                           variable=self.sfilter_var, orient=HORIZONTAL)
00370 
00371     for w in [text, line, fill, thresh, thresh_scale,
00372               tfilter, tfilter_scale, sfilter, sfilter_scale]:
00373       w.pack(side=TOP, anchor=W, fill=X) 
00374     return
00375 
00376   def on_threshold(self, var):
00377     self.threshold = self.threshold_var.get()
00378     return
00379 
00380   def on_sfilter(self, var):
00381     self.sfilter = self.sfilter_var.get()
00382     return
00383 
00384   def on_tfilter(self, var):
00385     self.tfilter = self.tfilter_var.get()
00386     return
00387 
00388   def line_toggle(self):
00389     if self.lrf_line_check.get() == "on":
00390       self.line_color = self.default_line_color
00391     else:
00392       self.line_color = ""
00393     return
00394 
00395   def fill_toggle(self):
00396     if self.lrf_fill_check.get() == "on":
00397       self.fill_color = self.default_fill_color
00398     else:
00399       self.fill_color = ""
00400     return
00401 
00402   def set_data_source(self, source):
00403     self.source = source
00404     return
00405 
00406   def set_value(self, data):
00407     self.rdata = data
00408     return
00409 
00410   def draw(self):
00411     self.delete()
00412     rpos = []
00413     rpos.append(self.translate(0, 0, 0, 0, 0))
00414     rpos.append(self.range_to_pos(self.rdata))
00415     self.poly_id = self.canvas.create_polygon(rpos,
00416                                               width = self.linewd,
00417                                               outline = self.line_color,
00418                                               fill = self.fill_color,
00419                                               smooth = 1,
00420                                               splinesteps = 5)
00421     return
00422 
00423   def range_to_pos(self, data):
00424     pos = []
00425     pre_d = 0
00426     
00427     tfilter = self.tfilter_check.get()
00428     sfilter = self.sfilter_check.get()
00429     thresh  = self.threshold_check.get()
00430 
00431     # Time-Filter
00432     if tfilter and len(data) == len(self.pre_data):
00433       for (n, d) in enumerate(data):
00434         k_t = self.tfilter
00435         data[n] = self.pre_data[n] * k_t + d * (1 - k_t)
00436 
00437     # Spacial Filter
00438     for (n, d) in enumerate(data):
00439       # Threshold
00440       if thresh and d < self.threshold:
00441         d = 10000 #pre_d
00442 
00443       if sfilter:
00444         k_s = self.sfilter
00445         d = pre_d * k_s + d * (1 - k_s)
00446       pre_d = d
00447             
00448       # n: step number
00449       # d: length data
00450       #deg = (n + self.offset_step) * self.angle_per_step + self.beg_angle
00451       #th = deg * math.pi / 180
00452       th = (n + self.offset_step) * self.angle_per_step + self.beg_angle
00453       x = d * math.cos(th)
00454       y = d * math.sin(th)
00455       pos.append(self.translate(x, y, 0, 0, 0))
00456     self.pre_data = data
00457     return pos
00458 
00459   def delete(self):
00460     if self.poly_id != None:
00461       self.canvas.delete(self.poly_id)
00462     return
00463 
00464   def update(self):
00465     if self.source != None:
00466       rdata = self.source.get_range_data()
00467       if len(rdata) != 0:
00468         self.rdata = rdata
00469 
00470       res = self.source.get_angular_res()
00471       if res:
00472         self.angle_per_step = res
00473 
00474       beg_angle = self.source.get_start_point()
00475       if beg_angle:
00476         self.beg_angle = beg_angle
00477 
00478       end_angle = self.source.get_end_point()
00479       if end_angle:
00480         self.end_angle = end_angle
00481 
00482     else:
00483       pass
00484     self.draw()
00485 
00486 
00487 
00488 #------------------------------------------------------------
00489 # TkLRFViewer main windows class
00490 #
00491 #------------------------------------------------------------
00492 class TkLRFViewer(Frame):
00493   def __init__(self, master=None, width=480, height=480):
00494     Frame.__init__(self, master)
00495 
00496     # canvas properties
00497     self.width = width
00498     self.height = height
00499     # zero of canvas
00500     self.x0 = width/2
00501     self.y0 = height/2
00502 
00503     self.wd = 150
00504 
00505     self.robots = {}
00506 
00507     self.robot = None
00508     self.postext = None
00509 
00510     self.scale = 1.0
00511     self.scale_var = DoubleVar()
00512     self.scale_var.set(self.scale)
00513 
00514     self.grid_pitch = 50
00515 
00516     self.tick = 0.1
00517     self.default_tick = 0.1
00518     self.tickscale_var = DoubleVar()
00519     self.tickscale_var.set(self.tick)
00520 
00521     self.axis_check = StringVar()
00522     self.axis_check.set("on")
00523     self.grid_check = StringVar()
00524     self.grid_check.set("on")
00525     self.rnames = {}
00526 
00527 
00528     self.init()
00529     self.pack()
00530 
00531 
00532     self.after(20, self.on_update)
00533     return
00534 
00535   def init(self):
00536     self.canvas = Canvas(self, bg="#000000",
00537                          width = self.width, height = self.height)
00538     self.canvas.pack(side=LEFT)
00539 
00540     self.can_grid = CanvasGrid(self.canvas, self.x0, self.y0,
00541                                self.width, self.height, self.grid_pitch,
00542                                "#aaaaaa", 1)
00543     self.can_axis = CanvasAxis(self.canvas, self.width, self.height,
00544                                "#ffffff", 1)
00545 
00546     self.frame = Frame(self)
00547     self.frame.pack(side=LEFT)
00548 
00549     # Screen control
00550     self.scrctrl_frame = Frame(self.frame, width=self.wd, height=300,
00551                                relief=GROOVE, bd=2)
00552     self.scrctrl_frame.pack(side=TOP, fill=X)
00553     self.create_scale(self.scrctrl_frame)
00554     self.create_checkbutton(self.scrctrl_frame)
00555 
00556 
00557     self.lrfctrl_frame = Frame(self.frame, width=self.wd, height=300,
00558                                relief=GROOVE, bd=2)
00559     self.lrfctrl_frame.pack(side=TOP, fill=X)
00560     self.lrf = LRFrange(self)
00561     self.lrf.create_ctrl(self.lrfctrl_frame)
00562 
00563     return
00564       
00565 
00566 
00567   def on_update(self):
00568     self.lrf.update()
00569     self.after(20, self.on_update)
00570     return
00571 
00572   def get_tick(self):
00573     return self.tick
00574   
00575   def get_canvas(self):
00576     return self.canvas
00577 
00578   def get_translation(self):
00579     return self.real_to_canvas
00580 
00581 
00582   #------------------------------------------------------------
00583   # Scale control set
00584   def create_scale(self, frame):
00585     dummy = Frame(frame, width=self.wd)
00586     dummy.pack(side=TOP)
00587     sl = Scale(frame, from_=0, to=10, resolution=0.01,
00588                label="Scale Factor", command=self.on_scale,
00589                variable=self.scale_var, orient=HORIZONTAL)
00590     bt = Button(frame, text="Reset Scale", command=self.reset_scale)
00591     sl.pack(side=TOP, fill=X)
00592     bt.pack(side=TOP, fill=X)
00593     return
00594 
00595   def on_scale(self, val):
00596     v =  float(val)
00597     if v == 0.0:
00598       pitch = 0
00599     else:
00600       pitch = self.grid_pitch/v
00601       self.scale = v
00602     self.can_grid.set_pitch(pitch)
00603     return
00604         
00605   def reset_scale(self):
00606     self.scale_var.set(1.)
00607     pitch = self.grid_pitch/1.0
00608     self.scale = 1.0
00609     self.can_grid.set_pitch(pitch)
00610     return
00611 
00612   def on_tickchange(self, val):
00613     v =  self.tickscale_var.get()
00614     if v == 0.0:
00615       self.tick = 0
00616     else:
00617       self.tick = v
00618     return
00619         
00620   def reset_tickscale(self):
00621     self.tick = self.default_tick
00622     self.tickscale_var.set(self.default_tick)
00623 
00624   # end of Scale widget set
00625   #------------------------------------------------------------
00626 
00627   #------------------------------------------------------------
00628   # Canvas control set
00629   def create_checkbutton(self, frame):
00630     axis = Checkbutton(frame, text="Axis",
00631                        onvalue="on", offvalue="off",
00632                        justify=LEFT, anchor=W,
00633                        variable=self.axis_check,
00634                        command=self.can_axis.toggle)
00635     grid = Checkbutton(frame, text="Grid",
00636                        onvalue="on", offvalue="off",
00637                        justify=LEFT, anchor=W,
00638                        variable=self.grid_check,
00639                        command=self.can_grid.toggle)
00640     for w in [axis, grid]:
00641       w.pack(side=TOP, anchor=W, fill=X)
00642 
00643   def on_rname_toggle(self):
00644     for r in self.rnames.keys():
00645       self.rnames[r].toggle()
00646     return
00647 
00648   # end of Canvas control set
00649   #------------------------------------------------------------
00650 
00651   #------------------------------------------------------------
00652   # 
00653   def real_to_canvas(self, x, y, dx, dy, dt):
00654     # Simulator coordinate system -> display coordinate system
00655     # x, y: original position
00656     # dx, dy, dt: translation and rotation vector
00657     # translation and rotation
00658     x_tmp = (math.cos(dt) * x - math.sin(dt) * y + dx)/self.scale
00659     y_tmp = (math.sin(dt) * x + math.cos(dt) * y + dy)/self.scale
00660     # align to canvas coordinate system (origin is center and y+ is upward)
00661     xo =  x_tmp  + self.x0
00662     yo = -y_tmp + self.y0
00663     return xo, yo
00664 
00665 import threading
00666 
00667 class test_data_creator(threading.Thread):
00668   def __init__(self, lrf, step = 681):
00669     threading.Thread.__init__(self)
00670     import time
00671     self.lrf = lrf
00672     self.step = step
00673     self.flag = True
00674     return
00675 
00676   def stop(self):
00677     self.flag = False
00678     return
00679 
00680   def run(self):
00681     import random
00682     data = [0] * 681
00683     pre = 0
00684     while self.flag:
00685       for i in range(681):
00686         if i % 5 == 0:
00687           data[i] = pre * 0.9 + random.randint(0, 255) * 0.1
00688           pre = data[i]
00689         else:
00690           data[i] = pre
00691           pre = data[i]
00692       self.lrf.set_value(data)
00693       time.sleep(0.1)
00694     
00695     return
00696 
00697 def main():
00698   m = TkLRFViewer(Tk())
00699   m.master.title("Laser Range Finder Viewer")
00700 
00701   mgr = OpenRTM_aist.Manager.init(sys.argv)
00702   mgr.activateManager()
00703   profile = OpenRTM_aist.Properties(defaults_str=lrfviewer_spec)
00704   mgr.registerFactory(profile, LRFViewer, OpenRTM_aist.Delete)
00705   mgr.runManager(True)
00706   lrf_rtc = mgr.createComponent("LRFViewer")
00707   m.lrf.set_data_source(lrf_rtc)
00708   m.mainloop()
00709   mgr.shutdown()
00710 
00711     
00712 if  __name__ == '__main__': main()


openrtm_aist_python
Author(s): Shinji Kurihara
autogenerated on Thu Aug 27 2015 14:17:28