56 if sys.version_info[0] < 3:
58 from Tkconstants
import *
59 from tkFileDialog
import *
63 from Tkconstants
import *
64 from tkFileDialog
import *
84 LaelapsWikiUrl =
"https://github.com/roadnarrows-robotics/laelaps/wiki" 88 "/prj/share/Laelaps/images",
89 "/usr/local/share/Laelaps/images",
90 "/prj/share/appkit/images",
91 "/usr/local/share/appkit/images" 101 'left_front':
'#aa0000',
102 'right_front':
'#aa6600',
103 'left_rear':
'#0066aa',
104 'right_rear':
'#0000aa',
114 _ctlrkey =
lambda pos: str.lower(pos) +
"_ctlr" 115 _motkey =
lambda pos: str.lower(pos) +
"_motor" 120 def dictConcat(d1, d2):
152 Frame.__init__(self, master=master, cnf=cnf, **kw)
155 self.master.title(
"Laelaps Motors")
157 self.
m_imageLoader = Utils.ImageLoader(py_pkg=
'Laelaps.images',
158 image_paths=ImagePath)
160 self.
m_icons[
'app_icon'] = self.m_imageLoader.load(
"icons/LaelapsIcon.png")
168 self.grid(row=0, column=0, padx=5, pady=5)
170 self.update_idletasks()
178 self.
t0 = time.time()
180 def perfMark(self, msg):
182 print "%.4f: %s" % (t1 - self.
t0, msg)
205 'Rear': SysConf.MotorCtlrAddrRear}
208 'front_ctlr': {
'left_motor':
'left_front',
'right_motor':
'right_front'},
209 'rear_ctlr': {
'left_motor':
'left_rear',
'right_motor':
'right_rear'}
214 'front_ctlr': {
'changes':
False,
'left_motor': {},
'right_motor': {}},
215 'rear_ctlr': {
'changes':
False,
'left_motor': {},
'right_motor': {}}
228 def dbgSeedFields(self):
230 ctlrkey = _ctlrkey(ctlrpos)
233 self.
m_var[ctlrkey][
'addr'].set(
"0x%02x" % (addr))
234 self.
m_var[ctlrkey][
'baudrate'].set(SysConf.MotorCtlrBaudRate)
235 self.
m_var[ctlrkey][
'model'].set(
"2x15a")
236 self.
m_var[ctlrkey][
'version'].set(
"4.1.6")
237 self.
m_var[ctlrkey][
'batt_max'].set(33.6)
238 self.
m_var[ctlrkey][
'batt_min'].set(8.1)
239 self.
m_var[ctlrkey][
'logic_max'].set(33.6)
240 self.
m_var[ctlrkey][
'logic_min'].set(5.0)
241 self.
m_var[ctlrkey][
'battery'].set(11.9)
242 self.
m_var[ctlrkey][
'logic'].set(12.0)
243 self.
m_var[ctlrkey][
'temp'].set(26.4)
244 self.
m_var[ctlrkey][
'alarm_temp'][
'w'][
'image'] = \
246 self.
m_var[ctlrkey][
'alarm_temp'][
'val'] = TriState[
'off']
247 self.
m_var[ctlrkey][
'alarm_batt_high'][
'w'][
'image'] = \
249 self.
m_var[ctlrkey][
'alarm_batt_high'][
'val'] = TriState[
'on']
252 motorkey = _motkey(motorpos)
254 self.
m_var[ctlrkey][motorkey][
'enc_type'].set(
"Quadrature")
255 self.
setPidParams(ctlrkey, motorkey, 1.0, 0.25, 0.5, 100000, 1000.0)
262 self.
m_icons[
'rn_logo'] = self.m_imageLoader.load(
"RNLogo48.png");
263 self.
m_icons[
'laelaps_logo'] = \
264 self.m_imageLoader.load(
"icon_laelaps_logo.png");
265 self.
m_icons[
'led_dark'] = self.m_imageLoader.load(
"icon_led_dark_16.png")
266 self.
m_icons[
'led_green'] = self.m_imageLoader.load(
"icon_led_green_16.png")
267 self.
m_icons[
'led_red'] = self.m_imageLoader.load(
"icon_led_red_16.png")
269 self.m_imageLoader.load(
'icon_chain_h_linked_16.png')
271 self.m_imageLoader.load(
'icon_chain_h_unlinked_16.png')
273 self.m_imageLoader.load(
'icon_chain_v_linked_16.png')
275 self.m_imageLoader.load(
'icon_chain_v_unlinked_16.png')
279 self.update_idletasks()
283 self.update_idletasks()
287 self.update_idletasks()
288 self.
perfMark(
"Created left buttons")
291 self.update_idletasks()
292 self.
perfMark(
"Created center panel")
295 self.update_idletasks()
309 self.m_wMenuFile.add_command(label=
"Save...", command=self.
notimpl)
310 self.m_wMenuFile.add_separator()
311 self.m_wMenuFile.add_command(label=
"Quit", command=self.
destroy)
312 self.m_wMenuBar.add_cascade(label=
"File", menu=self.
m_wMenuFile)
316 self.m_wMenuBar.add_cascade(label=
"Move", menu=self.
m_wMenuMove)
320 self.m_wMenuHelp.add_command(label=
"Online Laelaps Wiki",
321 command=
lambda aurl=LaelapsWikiUrl:webbrowser.open_new(LaelapsWikiUrl))
322 self.m_wMenuHelp.add_separator()
323 self.m_wMenuHelp.add_command(label=
"About...", command=self.
about)
324 self.m_wMenuBar.add_cascade(label=
"Help", menu=self.
m_wMenuHelp)
335 w[
'image'] = self.
m_icons[
'rn_logo']
341 w.grid(row=0, column=0, sticky=W)
345 w[
'font'] = (
'Helvetica', 16)
346 w[
'text'] =
"Laelaps Motors" 348 w[
'justify'] = CENTER
349 w.grid(row=0, column=1, sticky=E+W)
354 if self.
m_icons[
'laelaps_logo']:
355 w[
'image'] = self.
m_icons[
'laelaps_logo']
361 w.grid(row=0, column=2, sticky=E)
368 wframe[
'borderwidth'] = 0
369 wframe[
'relief'] =
'ridge' 370 wframe.grid(row=1, column=0, padx=1, pady=20, sticky=N+W+E)
376 w = self.
createButton(wframe,
"Apply",
"icons/icon_check.png",
378 w.grid(row=row, column=0, sticky=N+E+W)
383 "icons/icon_stop2.png", self.
stop)
384 w.grid(row=row, column=0, sticky=N+E+W)
388 w = self.
createButton(wframe,
"Save",
"icons/icon_floppy.png",
390 w.grid(row=row, column=0, sticky=N+E+W)
395 "icons/icon_info.png", self.
about)
396 w.grid(row=row, column=0, sticky=N+E+W)
402 w.grid(row=row, column=0, sticky=N+E+W)
409 wframe[
'borderwidth'] = 0
410 wframe[
'relief'] =
'ridge' 411 wframe.grid(row=1, column=1, columnspan=2,
412 padx=1, pady=3, sticky=N+W+E)
415 self.
perfMark(
"Created status panel")
418 self.
perfMark(
"Created velocity tuning panel")
420 self.update_idletasks()
421 width = wframe.winfo_width()
434 wframe = Frame(parent)
435 wframe[
'relief'] =
'flat' 436 wframe.grid(row=row, column=col, padx=1, pady=3, sticky=N+W+E)
454 wframe = LabelFrame(parent)
455 wframe[
'text'] =
"%s Motor Controller State" % (ctlrpos)
456 wframe[
'font'] = (
'Helvetica', 12)
457 wframe[
'fg'] = UIColors[
'focus']
458 wframe[
'borderwidth'] = 2
459 wframe[
'relief'] =
'ridge' 460 wframe.grid(row=row, column=col, padx=1, pady=1, sticky=N+W+E)
464 ctlrkey = _ctlrkey(ctlrpos)
467 wcfgLabel = {
'anchor': E,
'justify': RIGHT}
468 gcfgLabel = {
'padx': (5,2),
'pady': 2,
'sticky': E}
469 wcfgValue = {
'anchor': W,
'justify': LEFT,
'relief':
'solid'}
470 gcfgValue = {
'padx': (0,2),
'pady': 1,
'sticky': W}
476 dictConcat({
'text':
'Address:'}, wcfgLabel),
477 dictConcat({
'row': 0,
'column': col}, gcfgLabel))
480 self.
m_var[ctlrkey][
'addr'] = StringVar();
482 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'addr'],
'width': 8},
484 dictConcat({
'row': 0,
'column': col+1}, gcfgValue))
488 dictConcat({
'text':
'Baudrate:'}, wcfgLabel),
489 dictConcat({
'row': 1,
'column': col}, gcfgLabel))
492 self.
m_var[ctlrkey][
'baudrate'] = IntVar()
494 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'baudrate'],
495 'width': 8}, wcfgValue),
496 dictConcat({
'row': 1,
'column': col+1}, gcfgValue))
502 dictConcat({
'text':
'Model:'}, wcfgLabel),
503 dictConcat({
'row': 0,
'column': col}, gcfgLabel))
506 self.
m_var[ctlrkey][
'model'] = StringVar()
508 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'model'],
'width': 10},
510 dictConcat({
'row': 0,
'column': col+1}, gcfgValue))
514 dictConcat({
'text':
'Version:'}, wcfgLabel),
515 dictConcat({
'row': 1,
'column': col}, gcfgLabel))
518 self.
m_var[ctlrkey][
'version'] = StringVar()
520 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'version'],
521 'width': 10}, wcfgValue),
522 dictConcat({
'row': 1,
'column': col+1}, gcfgValue))
528 dictConcat({
'text':
'Max Battery (V):'}, wcfgLabel),
529 dictConcat({
'row': 0,
'column': col}, gcfgLabel))
532 self.
m_var[ctlrkey][
'batt_max'] = DoubleVar()
534 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'batt_max'],
535 'width': 5}, wcfgValue),
536 dictConcat({
'row': 0,
'column': col+1}, gcfgValue))
540 dictConcat({
'text':
'Min Battery (V):'}, wcfgLabel),
541 dictConcat({
'row': 1,
'column': col}, gcfgLabel))
544 self.
m_var[ctlrkey][
'batt_min'] = DoubleVar()
546 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'batt_min'],
547 'width': 5}, wcfgValue),
548 dictConcat({
'row': 1,
'column': col+1}, gcfgValue))
554 dictConcat({
'text':
'Max Logic (V):'}, wcfgLabel),
555 dictConcat({
'row': 0,
'column': col}, gcfgLabel))
558 self.
m_var[ctlrkey][
'logic_max'] = DoubleVar()
560 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'logic_max'],
561 'width': 5}, wcfgValue),
562 dictConcat({
'row': 0,
'column': col+1}, gcfgValue))
566 dictConcat({
'text':
'Min Logic (V):'}, wcfgLabel),
567 dictConcat({
'row': 1,
'column': col}, gcfgLabel))
570 self.
m_var[ctlrkey][
'logic_min'] = DoubleVar()
572 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'logic_min'],
573 'width': 5}, wcfgValue),
574 dictConcat({
'row': 1,
'column': col+1}, gcfgValue))
580 dictConcat({
'text':
'Battery (V):'}, wcfgLabel),
581 dictConcat({
'row': 0,
'column': col}, gcfgLabel))
584 self.
m_var[ctlrkey][
'battery'] = DoubleVar()
586 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'battery'],
'width': 5},
588 dictConcat({
'row': 0,
'column': col+1}, gcfgValue))
592 dictConcat({
'text':
'Logic (V):'}, wcfgLabel),
593 dictConcat({
'row': 1,
'column': col}, gcfgLabel))
596 self.
m_var[ctlrkey][
'logic'] = DoubleVar()
598 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'logic'],
599 'width': 5}, wcfgValue),
600 dictConcat({
'row': 1,
'column': col+1}, gcfgValue))
606 dictConcat({
'text':
'Temp (C):'}, wcfgLabel),
607 dictConcat({
'row': 0,
'column': col}, gcfgLabel))
610 self.
m_var[ctlrkey][
'temp'] = DoubleVar()
612 dictConcat({
'textvariable': self.
m_var[ctlrkey][
'temp'],
613 'width': 6}, wcfgValue),
614 dictConcat({
'row': 0,
'column': col+1}, gcfgValue))
618 wframe = Frame(wframe)
619 wframe.grid(row=3, column=0, columnspan=col, padx=1, pady=5, sticky=N+W+E)
627 wframe = Frame(wframe)
628 wframe.grid(row=row, column=0, columnspan=col, padx=1, pady=5, sticky=N+W+E)
641 wframe = Frame(parent)
642 wframe[
'relief'] =
'flat' 643 wframe.grid(row=row, column=col, padx=1, pady=3, sticky=N+W+E)
651 'fg': UIColors[
'focus'],
653 {
'row': row,
'column': col,
'padx': 1,
'pady': 2,
'sticky': E})
659 'Left Motor\nOver Current',
'alarm_lmoc')
665 'Right Motor\nOver Current',
'alarm_rmoc')
671 'Battery\nLow Volt',
'alarm_batt_low')
677 'Battery\nHigh Volt',
'alarm_batt_high')
683 'Logic\nLow Volt',
'alarm_logic_low')
689 'Logic\nHigh Volt',
'alarm_logic_high')
695 'Over\nTemperature',
'alarm_temp')
701 'Emergency\nStopped',
'alarm_estop')
714 wframe = Frame(parent)
715 wframe[
'borderwidth'] = 1
716 wframe[
'relief'] =
'solid' 717 wframe.grid(row=row, column=col, padx=0, pady=1, sticky=N+W+E)
723 {
'text': text,
'justify': CENTER,
'anchor': CENTER},
724 {
'row':row,
'column':col,
'padx':(1, 1),
'pady':2,
'sticky':W+S+E})
727 {
'image': self.
m_icons[
'led_dark'],
'justify': CENTER,
'anchor': CENTER},
728 {
'row':row,
'column':col+1,
'padx':1,
'pady':(2,4),
'sticky':W+S+E})
730 self.
m_var[ctlrkey][key] = {
'w': w,
'val': TriState[
'none']}
744 {
'text':
"%s Motor:" % (motorpos),
745 'font': (
'Helvetica', 10),
746 'fg': UIColors[
'focus'],
748 {
'row': row,
'column': col,
'padx': 1,
'pady': 2,
'sticky': N+W+E})
750 motorkey = _motkey(motorpos)
753 wcfgLabel = {
'anchor': E,
'justify': RIGHT}
754 gcfgLabel = {
'padx': (5,2),
'pady': 2,
'sticky': E}
755 wcfgValue = {
'anchor': W,
'justify': LEFT,
'relief':
'solid'}
756 gcfgValue = {
'padx': (0,2),
'pady': 1,
'sticky': W}
762 dictConcat({
'text':
'Encoder Type:'}, wcfgLabel),
763 dictConcat({
'row': row,
'column': col}, gcfgLabel))
766 self.
m_var[ctlrkey][motorkey][
'enc_type'] = StringVar()
768 dictConcat({
'textvariable': self.
m_var[ctlrkey][motorkey][
'enc_type'],
769 'width': 10}, wcfgValue),
770 dictConcat({
'row': row,
'column': col+1}, gcfgValue))
776 dictConcat({
'text':
'Amps:'}, wcfgLabel),
777 dictConcat({
'row': row,
'column': col}, gcfgLabel))
780 self.
m_var[ctlrkey][motorkey][
'amps'] = DoubleVar()
782 dictConcat({
'textvariable': self.
m_var[ctlrkey][motorkey][
'amps'],
783 'width': 8}, wcfgValue),
784 dictConcat({
'row': row,
'column': col+1}, gcfgValue))
790 dictConcat({
'text':
'Encoder:'}, wcfgLabel),
791 dictConcat({
'row': row,
'column': col}, gcfgLabel))
794 self.
m_var[ctlrkey][motorkey][
'encoder'] = IntVar()
796 dictConcat({
'textvariable': self.
m_var[ctlrkey][motorkey][
'encoder'],
797 'width': 12}, wcfgValue),
798 dictConcat({
'row': row,
'column': col+1}, gcfgValue))
804 dictConcat({
'text':
'Speed (QPPS):'}, wcfgLabel),
805 dictConcat({
'row': row,
'column': col}, gcfgLabel))
808 self.
m_var[ctlrkey][motorkey][
'speed'] = IntVar()
810 dictConcat({
'textvariable': self.
m_var[ctlrkey][motorkey][
'speed'],
811 'width': 8}, wcfgValue),
812 dictConcat({
'row': row,
'column': col+1}, gcfgValue))
824 wframe = Frame(parent)
825 wframe[
'relief'] =
'flat' 826 wframe.grid(row=row, column=col, padx=1, pady=1, sticky=N+W+E)
844 wframe = LabelFrame(parent)
845 wframe[
'text'] =
"%s Velocity Tuning" % (ctlrpos)
846 wframe[
'font'] = (
'Helvetica', 12)
847 wframe[
'fg'] = UIColors[
'focus']
848 wframe[
'borderwidth'] = 2
849 wframe[
'relief'] =
'ridge' 850 wframe.grid(row=row, column=col, padx=1, pady=5, sticky=N+W+E)
852 ctlrkey = _ctlrkey(ctlrpos)
854 wcfgLabel = {
'anchor': E,
'justify': RIGHT}
855 gcfgLabel = {
'padx': (5,2),
'pady': 2,
'sticky': E}
856 wcfgValue = {
'anchor': W,
'justify': LEFT,
'relief':
'solid'}
857 gcfgValue = {
'padx': (0,5),
'pady': 1,
'sticky': W}
880 wframe = LabelFrame(parent)
881 wframe[
'text'] =
'PID Parameters' 883 wframe[
'fg'] = UIColors[
'focus'],
884 wframe[
'borderwidth'] = 2
885 wframe[
'relief'] =
'ridge' 886 wframe.grid(row=row, column=col, padx=(5,1), pady=(5,0), sticky=N+W+E)
888 wcfgLabel = {
'anchor': E,
'justify': RIGHT}
889 gcfgLabel = {
'padx': (5,2),
'pady': 2,
'sticky': E}
890 wcfgValue = {
'anchor': W,
'justify': LEFT,
'relief':
'solid'}
891 gcfgValue = {
'padx': (0,5),
'pady': 1,
'sticky': W}
900 dictConcat({
'text':
'P:'}, wcfgLabel),
901 dictConcat({
'row': row,
'column': col}, gcfgLabel))
906 dictConcat({
'text':
'I:'}, wcfgLabel),
907 dictConcat({
'row': row,
'column': col}, gcfgLabel))
912 dictConcat({
'text':
'D:'}, wcfgLabel),
913 dictConcat({
'row': row,
'column': col}, gcfgLabel))
918 dictConcat({
'text':
'Max QPPS:'}, wcfgLabel),
919 dictConcat({
'row': row,
'column': col}, gcfgLabel))
924 dictConcat({
'text':
'Max Accel:'}, wcfgLabel),
925 dictConcat({
'row': row,
'column': col}, gcfgLabel))
933 motorkey = _motkey(motorpos)
938 {
'text': motorpos+
' Motor',
'anchor': W,
'justify': LEFT},
939 {
'row': row,
'column': col,
'padx': (0,1),
'pady': 2,
'sticky': W})
945 self.
m_var[ctlrkey][motorkey][key] = DoubleVar()
948 {
'textvariable': self.
m_var[ctlrkey][motorkey][key],
949 'width': 8,
'justify': LEFT,
'relief':
'sunken'},
950 dictConcat({
'row': row,
'column': col}, gcfgValue))
956 self.
m_var[ctlrkey][motorkey][key] = DoubleVar()
959 {
'textvariable': self.
m_var[ctlrkey][motorkey][key],
960 'width': 8,
'justify': LEFT,
'relief':
'sunken'},
961 dictConcat({
'row': row,
'column': col}, gcfgValue))
967 self.
m_var[ctlrkey][motorkey][key] = DoubleVar()
970 {
'textvariable': self.
m_var[ctlrkey][motorkey][key],
971 'width': 8,
'justify': LEFT,
'relief':
'sunken'},
972 dictConcat({
'row': row,
'column': col}, gcfgValue))
978 self.
m_var[ctlrkey][motorkey][key] = DoubleVar()
981 {
'textvariable': self.
m_var[ctlrkey][motorkey][key],
982 'width': 8,
'justify': LEFT,
'relief':
'sunken'},
983 dictConcat({
'row': row,
'column': col}, gcfgValue))
987 key =
'vel_pid_max_accel' 989 self.
m_var[ctlrkey][motorkey][key] = DoubleVar()
992 {
'textvariable': self.
m_var[ctlrkey][motorkey][key],
993 'width': 8,
'justify': LEFT,
'relief':
'sunken'},
994 dictConcat({
'row': row,
'column': col}, gcfgValue))
1000 self.
m_var[ctlrkey][motorkey][
'vel_pid_params'] = PidParams.VelPidParams()
1004 dictConcat({
'text':
u'\u21b0'}, wcfgLabel),
1005 {
'row': 1,
'column': col,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1009 dictConcat({
'text':
''}, wcfgLabel),
1010 {
'row': 2,
'column': col,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1014 icon_key =
'linked_v' 1017 w[
'image'] = self.
m_icons[icon_key]
1024 w[
'anchor'] = CENTER
1026 self.
m_var[ctlrkey][
'pid_linked'] = {
'w': w,
'val':
True};
1027 w[
'command'] = cblink(ctlrkey)
1028 w.grid(row=3, column=col)
1032 dictConcat({
'text':
''}, wcfgLabel),
1033 {
'row': 4,
'column': col,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1037 dictConcat({
'text':
u'\u21b2'}, wcfgLabel),
1038 {
'row': 5,
'column': col,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1049 wframe = LabelFrame(parent)
1050 wframe[
'text'] =
'Setpoints' 1052 wframe[
'fg'] = UIColors[
'focus'],
1053 wframe[
'borderwidth'] = 2
1054 wframe[
'relief'] =
'ridge' 1055 wframe.grid(row=row, column=col, padx=(5,1), pady=(5,0),
1058 wcfgLabel = {
'anchor': E,
'justify': RIGHT}
1059 gcfgLabel = {
'padx': (5,2),
'pady': 2,
'sticky': E}
1060 wcfgValue = {
'anchor': W,
'justify': LEFT,
'relief':
'solid'}
1061 gcfgValue = {
'padx': (0,5),
'pady': 1,
'sticky': W}
1065 def cbspd(ck, mk, fk):
return lambda: self.
cbVelSetpoint(ck, mk, fk, 0)
1066 def cbpct(ck, mk, fk):
return lambda v: self.
cbVelSetpoint(ck, mk, fk, v)
1069 motorkey = _motkey(motorpos)
1074 dictConcat({
'text': motorpos+
' Motor:'}, wcfgLabel),
1075 {
'row': row,
'column': col,
'padx': (2,2),
'pady': (1,0),
'sticky': W})
1079 key =
'setpoint_speed' 1081 self.
m_var[ctlrkey][motorkey][key] = IntVar()
1084 {
'textvariable': self.
m_var[ctlrkey][motorkey][key],
1085 'validate':
'focusout',
1086 'validatecommand': cbspd(ctlrkey, motorkey, key),
1087 'width': 8,
'justify': LEFT,
'relief':
'sunken'},
1088 {
'row': row,
'column': col,
1089 'padx': (2,2),
'pady': (1,0),
'sticky': E})
1093 key =
'setpoint_percent' 1095 self.
m_var[ctlrkey][motorkey][key] = IntVar()
1099 {
'variable': self.
m_var[ctlrkey][motorkey][key],
1100 'command': cbpct(ctlrkey, motorkey, key),
1101 'from_': -100,
'to': 100,
'resolution': 1,
'orient': HORIZONTAL,
1102 'tickinterval': 50,
'length': 210},
1103 {
'row': row,
'column': col,
'columnspan': 2,
1104 'padx': (2,2),
'pady': (0,0),
'sticky': W})
1109 if motorpos ==
'Left':
1111 dictConcat({
'text':
''}, wcfgLabel),
1112 {
'row': row,
'column': col,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1118 dictConcat({
'text':
u'\u21b0'}, wcfgLabel),
1119 {
'row': 0,
'column': 2,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1123 dictConcat({
'text':
''}, wcfgLabel),
1124 {
'row': 1,
'column': 2,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1128 icon_key =
'linked_v' 1131 w[
'image'] = self.
m_icons[icon_key]
1138 w[
'anchor'] = CENTER
1140 self.
m_var[ctlrkey][
'setpoint_linked'] = {
'w': w,
'val':
True};
1141 w[
'command'] = cblink(ctlrkey)
1142 w.grid(row=2, column=2)
1146 dictConcat({
'text':
''}, wcfgLabel),
1147 {
'row': 3,
'column': 2,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1151 dictConcat({
'text':
u'\u21b2'}, wcfgLabel),
1152 {
'row': 4,
'column': 2,
'padx': (2,2),
'pady': (1,1),
'sticky': W})
1163 wframe = LabelFrame(parent)
1164 wframe[
'text'] =
"Real-Time Velocity Plot" 1165 wframe[
'font'] = (
'Helvetica', 12)
1166 wframe[
'fg'] = UIColors[
'focus']
1167 wframe[
'borderwidth'] = 2
1168 wframe[
'relief'] =
'ridge' 1169 wframe.grid(row=row, column=col, columnspan=2, padx=1, pady=1, sticky=N+W+E)
1172 self.
perfMark(
"Created plot controls")
1175 self.
perfMark(
"Created plot canvas")
1187 wframe = Frame(parent)
1188 wframe[
'borderwidth'] = 0
1189 wframe[
'relief'] =
'flat' 1190 wframe.grid(row=row, column=col, padx=1, pady=1, sticky=N+W+E)
1195 def cben(ck, mk, fk):
return lambda: self.
cbEnDisVelPlot(ck, mk, fk)
1197 ctlrkey =
"front_ctlr" 1198 motorkey =
"left_motor" 1199 fieldkey =
"plotvel" 1203 {
'text':
' ',
'bg': UIColors[
'left_front'],
1204 'anchor': W,
'justify': LEFT},
1205 {
'row': row,
'column': col,
'padx': (5,1),
'pady': (1,1),
'sticky': W})
1208 self.
m_var[ctlrkey][motorkey][fieldkey] = IntVar();
1211 {
'text':
'Left Front',
'command': cben(ctlrkey, motorkey, fieldkey),
1212 'variable': self.
m_var[ctlrkey][motorkey][fieldkey],
1213 'anchor': W,
'justify': LEFT},
1214 {
'row': row,
'column': col+1,
'padx': (1,5),
'pady': (1,1),
'sticky': W})
1218 motorkey =
"right_motor" 1222 {
'text':
' ',
'bg': UIColors[
'right_front'],
1223 'anchor': W,
'justify': LEFT},
1224 {
'row': row,
'column': col,
'padx': (5,1),
'pady': (1,1),
'sticky': W})
1227 self.
m_var[ctlrkey][motorkey][fieldkey] = IntVar();
1230 {
'text':
'Right Front',
'command': cben(ctlrkey, motorkey, fieldkey),
1231 'variable': self.
m_var[ctlrkey][motorkey][fieldkey],
1232 'anchor': W,
'justify': LEFT},
1233 {
'row': row,
'column': col+1,
'padx': (1,5),
'pady': (1,1),
'sticky': W})
1237 ctlrkey =
"rear_ctlr" 1238 motorkey =
"left_motor" 1242 {
'text':
' ',
'bg': UIColors[
'left_rear'],
1243 'anchor': W,
'justify': LEFT},
1244 {
'row': row,
'column': col,
'padx': (5,1),
'pady': (1,1),
'sticky': W})
1247 self.
m_var[ctlrkey][motorkey][fieldkey] = IntVar();
1250 {
'text':
'Left Rear',
'command': cben(ctlrkey, motorkey, fieldkey),
1251 'variable': self.
m_var[ctlrkey][motorkey][fieldkey],
1252 'anchor': W,
'justify': LEFT},
1253 {
'row': row,
'column': col+1,
'padx': (1,5),
'pady': (1,1),
'sticky': W})
1257 motorkey =
"right_motor" 1261 {
'text':
' ',
'bg': UIColors[
'right_rear'],
1262 'anchor': W,
'justify': LEFT},
1263 {
'row': row,
'column': col,
'padx': (5,1),
'pady': (1,1),
'sticky': W})
1266 self.
m_var[ctlrkey][motorkey][fieldkey] = IntVar();
1269 {
'text':
'Right Rear',
'command': cben(ctlrkey, motorkey, fieldkey),
1270 'variable': self.
m_var[ctlrkey][motorkey][fieldkey],
1271 'anchor': W,
'justify': LEFT},
1272 {
'row': row,
'column': col+1,
'padx': (1,5),
'pady': (1,1),
'sticky': W})
1276 self.update_idletasks()
1277 width = wframe.winfo_width()
1291 wframe = Frame(parent)
1292 wframe[
'borderwidth'] = 0
1293 wframe[
'relief'] =
'ridge' 1294 wframe.grid(row=row, column=col, padx=1, pady=1, sticky=N+W+E)
1300 wCanvas = Canvas(wframe, height=height, width=width)
1301 wCanvas.grid(row=0, column=0)
1303 self.
m_plotVel = VelPlot.VelPlot(wCanvas, width, height,
1304 [
'left_front',
'right_front',
'left_rear',
'right_rear'],
1307 self.update_idletasks()
1323 for wdesc
in wdescTbl:
1324 self.
makeWidget(parent, wdesc[
'widget'], wdesc[
'wcfg'], wdesc[
'gcfg'])
1337 w = widget(parent, **wcfg)
1345 wframe = Frame(self)
1346 wframe[
'borderwidth'] = 2
1347 wframe[
'relief'] =
'ridge' 1348 wframe.grid(row=2, column=0, columnspan=3, padx=1, pady=3, sticky=N+E+W+S)
1351 self.m_varStatus.set(
"Calibration required.")
1358 self.m_wStatusBar.grid(row=0, column=0, padx=3, pady=3, sticky=N+E+W+S)
1365 self.
m_wBttn[key][
'state'] = state
1379 key = str.lower(text.replace(
"\n",
"_"))
1380 self.
m_icons[key] = self.m_imageLoader.load(imagefile)
1384 w[
'image'] = self.
m_icons[key]
1385 w[
'compound'] = LEFT
1391 w[
'anchor'] = CENTER
1394 w[
'command'] = command
1408 emsg =
"Window function not implemented yet." 1435 dlg = AboutDlg(master=self, info=prodInfo, app_ver=AppVersion)
1437 def cbLinkAllPids(self):
1440 def cbLinkCtlrPids(self, ck):
1442 if self.m_var[ck][fk][
'val']:
1443 self.m_var[ck][fk][
'w'][
'image'] = self.m_icons[
'unlinked_v']
1444 self.m_var[ck][fk][
'val'] =
False 1446 self.m_var[ck][fk][
'w'][
'image'] = self.m_icons[
'linked_v']
1447 self.m_var[ck][fk][
'val'] =
True 1449 def cbLinkAllSetpoints(self):
1452 def cbLinkCtlrSetpoints(self, ck):
1453 fk =
'setpoint_linked' 1454 if self.m_var[ck][fk][
'val']:
1455 self.m_var[ck][fk][
'w'][
'image'] = self.m_icons[
'unlinked_v']
1456 self.m_var[ck][fk][
'val'] =
False 1458 self.m_var[ck][fk][
'w'][
'image'] = self.m_icons[
'linked_v']
1459 self.m_var[ck][fk][
'val'] =
True 1474 val = self.
m_var[ck][mk][fk].get()
1476 self.m_plotVel.setpoint(pk, val)
1490 val = self.
m_var[ck][mk][fk].get()
1491 sp = self.
m_var[ck][mk][
'setpoint_speed'].get()
1493 self.m_plotVel.enable(name, sp)
1495 self.m_plotVel.disable(name)
1497 def setPidParams(self, ck, mk, Kp, Ki, Kd, maxQpps, maxAccel):
1499 self.
m_var[ck][mk][
'vel_pid_p'].set(Kp)
1500 self.
m_var[ck][mk][
'vel_pid_i'].set(Ki)
1501 self.
m_var[ck][mk][
'vel_pid_d'].set(Kd)
1502 self.
m_var[ck][mk][
'vel_pid_qpps'].set(maxQpps)
1503 self.
m_var[ck][mk][
'vel_pid_max_accel'].set(maxAccel)
1506 self.
m_var[ck][mk][
'vel_pid_params'].m_Kp = Kp
1507 self.
m_var[ck][mk][
'vel_pid_params'].m_Ki = Ki
1508 self.
m_var[ck][mk][
'vel_pid_params'].m_Kd = Kd
1509 self.
m_var[ck][mk][
'vel_pid_params'].m_maxQpps = maxQpps
1510 self.
m_var[ck][mk][
'vel_pid_params'].m_maxAccel = maxAccel
1520 rospy.wait_for_service(
"laelaps_control/get_product_info", timeout=5)
1521 except rospy.ROSException, e:
1522 self.
showError(
'Get product info: ' + e.message +
'.')
1525 get_product_info = rospy.ServiceProxy(
1526 'laelaps_control/get_product_info',
1528 rsp = get_product_info()
1530 except rospy.ServiceException, e:
1531 self.
showError(
"Get product info request failed: %s." % (e.message))
1542 rospy.wait_for_service(
"laelaps_control/get_product_info", timeout=5)
1543 except rospy.ROSException, e:
1544 rospy.logerr(
"Get product info: %s." % (e.message))
1547 get_product_info = rospy.ServiceProxy(
1548 'laelaps_control/get_product_info',
1550 rsp = get_product_info()
1551 name = rsp.i.hostname
1552 except rospy.ServiceException, e:
1553 rospy.logerr(
"Get product info request failed: %s." % (e.message))
1571 self.m_varStatus.set(msg)
1582 self.m_varStatus.set(msg)
1594 w[
'state'] =
'normal' 1597 w[
'state'] =
'readonly' 1622 self.master.title(
"Laelaps Control Panel - %s" % (self.
m_botName))
1623 self.
m_wTopHeading[
'text'] =
"Laelaps Control Panel - %s" % \
1661 self.
_Argv0 = os.path.basename(__file__)
1671 print "%s: %s" % (self.
_Argv0, emsg)
1673 print "%s: error" % (self.
_Argv0)
1674 print "Try '%s --help' for more information." % (self.
_Argv0)
1683 Options and arguments: 1684 -h, --help : Display this help and exit. 1700 self.
_Argv0 = os.path.basename(kwargs.get(
'argv0', __file__))
1703 kwargs[
'debug'] =
False 1707 opts, args = getopt.getopt(argv[1:],
"?h",
1709 except getopt.error, msg:
1711 for opt, optarg
in opts:
1712 if opt
in (
'-h',
'--help',
'-?'):
1728 self.m_win.showInfo(
"Initializing interface to Laelaps.")
1730 self.m_win.showInfo(
"Laelaps motor interface initialized.")
1740 def run(self, argv=None, **kwargs):
1756 root.protocol(
'WM_DELETE_WINDOW', root.destroy)
1758 root.columnconfigure(0, weight=1)
1759 root.rowconfigure(0, weight=1)
1765 self.m_win.mainloop()
1773 if __name__ ==
'__main__':
1775 sys.exit( app.run() );
def createMotorPanel(self, parent, row, col, ctlrkey, motorpos)
Create motor controller motor panel.
def showError(self, msg)
Show error message on status bar.
def setPidParams(self, ck, mk, Kp, Ki, Kd, maxQpps, maxAccel)
def createPidParamsPanel(self, parent, row, col, ctlrkey)
Create motor controller motors PID parameters panel.
Unit test command-line exception class.
def apply(self)
Apply tuning tweaks to controllers callback.
def getLaelapsName(self)
Get Laelaps name.
def __init__(self, msg)
Constructor.
def printUsageErr(self, emsg)
Print usage error.
def cbVelSetpoint(self, ck, mk, fk, v)
Setpoint changed callback.
def createWidgets(self)
Create gui widgets with supporting data and show.
def createVelTuningPanel(self, parent, row, col)
Create velocity tuning panel.
def cbLinkCtlrSetpoints(self, ck)
def updateButtonState(self, keys, state)
Update button activation states.
def createMotorsTuningPanel(self, parent, row, col, ctlrpos)
Create motor controller motors tuning panel.
def showEntry(self, w, var, val, fg='black')
Show text on read-only entry.
def cbEnDisVelPlot(self, ck, mk, fk)
Enable/disable velocity plot.
def about(self)
Show about dialog callback.
def getOptions(self, argv=None, kwargs)
Get command-line options.
def createPlotControls(self, parent, row, col)
Create real-time plot controls.
def createStatusBar(self)
Create gui status bar at bottom of gui window.
def createAlarmsPanel(self, parent, row, col, ctlrkey)
Create motor controller alarm panel.
def createHeading(self)
Create top gui heading.
def createCenterPanel(self)
Create robot status and joint state center panel.
def createMenu(self)
Create menu.
def run(self, argv=None, kwargs)
Run application.
Window class supporting application.
def __init__(self)
Constructor.
def cbLinkCtlrPids(self, ck)
def makeWidgets(self, parent, wdescTbl)
Make widgets from widget description table.
msg
error message attribute
def createSetpointsPanel(self, parent, row, col, ctlrkey)
Create motor controller motor velocity setpoints panel.
def createPlotCanvas(self, parent, row, col, width)
Create real-time plot canvas.
def createStatusPanel(self, parent, row, col)
Create motor status panel.
def initRobot(self)
Initialize interface to hek_robot.
def makeWidget(self, parent, widget, wcfg, gcfg)
Make widget.
def initData(self, kw)
Initialize class state data.
def stop(self)
Stop Laelaps.
def updateStatus(self)
Update motor status.
def printUsage(self)
Print Command-Line Usage Message.
def createAlarmWidget(self, parent, row, col, ctlrkey, text, key)
Create alarm widgets and initalize alarm db.
def createLeftButtons(self)
Create gui left hand side buttons.
def destroy(self)
Destroy window callback.
def createButton(self, parent, text, imagefile, command, fg='black')
Create button.
def notimpl(self)
Not implemented callback.
def save(self)
Save tuning to tuning file and controller EEPROM callback.
def showInfo(self, msg)
Show information message on status bar.
def createMotorCtlrPanel(self, parent, row, col, ctlrpos)
Create motor controller panel.
def alignToJustify(self, align)
Map alignment value to justify equivalent.
def finalInits(self)
Final window initializations.
def createPlotPanel(self, parent, row, col, width)
Create real-time plot panel.
def __init__(self, master=None, cnf={}, kw)
Constructor.
def getProductInfo(self)
Get product information.