百度地图轨迹回放,自定义路书,边走边画线

本文介绍了一种基于百度地图API的轨迹回放方法,通过自定义路径实现动态绘制轨迹线,支持开始、停止、暂停等功能,并展示了完整的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:https://www.cnblogs.com/syj2016/p/5685294.html

百度地图轨迹回放,自定义路书,边走边画线

在原有的百度路书的基础上,做了修改,使其能实现边走边画线的需求.

源代码如下,其中您的密钥要换成自己的,如果不换,则需要粘贴到百度API示例里面的GPS路书的编辑器中才能运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<html lang= "en" >
<head>
     <meta charset= "utf-8"  />
     <title>轨迹回放(路书)</title>
     <style type= "text/css" >
         body, html{width: 100%;height: 100%;margin:0;font-family: "微软雅黑" ;}
         #map_canvas{width:100%;height:500px;}
         #result {width:100%}
     </style>
     <script src= "http://api.map.baidu.com/api?v=2.0&ak=您的密钥" ></script>
     <script type= "text/javascript"  src= "http://api.map.baidu.com/library/LuShu/1.2/src/LuShu_min.js" ></script>
</head>
<body>
     <div id= "map_canvas" ></div>
     <div id= "result" ></div>
     <button id= "run" >开始</button>
     <button id= "stop" >停止</button>
     <button id= "pause" >暂停</button>
     <button id= "hide" >隐藏信息窗口</button>
     <button id= "show" >展示信息窗口</button>
     <script>
     var  marker;
     var  map =  new  BMap.Map( 'map_canvas' );
     map.enableScrollWheelZoom();
     map.centerAndZoom();
     var  lushu;
     // 实例化一个驾车导航用来生成路线
   //  var drv = new BMap.DrivingRoute('北京', {
    //     onSearchComplete: function(res) {
     //        if (drv.getStatus() == BMAP_STATUS_SUCCESS) {
               //  var plan = res.getPlan(0);
                // var arrPois =[];
//                for(var j=0;j<plan.getNumRoutes();j++){
                  //   var route = plan.getRoute(j);
                  //   arrPois= arrPois.concat(route.getPath());
                // }
       var  arrPois=[ new  BMap.Point(116.403984,39.914004), new  BMap.Point(116.402116,39.913938), new  BMap.Point(116.402116,39.913938), new  BMap.Point(116.402046,39.913928), new  BMap.Point(116.401856,39.913927), new  BMap.Point(116.401547,39.913926), new  BMap.Point(116.401008,39.913923), new  BMap.Point(116.400599,39.913921), new  BMap.Point(116.39999,39.913908), new  BMap.Point(116.399471,39.913875), new  BMap.Point(116.399042,39.913852), new  BMap.Point(116.398035,39.913835), new  BMap.Point(116.395593,39.913683), new  BMap.Point(116.395383,39.913681), new  BMap.Point(116.395114,39.913658), new  BMap.Point(116.394666,39.913664), new  BMap.Point(116.390265,39.91349), new  BMap.Point(116.38925,39.913456), new  BMap.Point(116.388931,39.913461), new  BMap.Point(116.388643,39.913457), new  BMap.Point(116.388335,39.913463), new  BMap.Point(116.386455,39.913424), new  BMap.Point(116.386166,39.913429), new  BMap.Point(116.385898,39.913425), new  BMap.Point(116.384327,39.913399), new  BMap.Point(116.384168,39.913396), new  BMap.Point(116.38384,39.913391), new  BMap.Point(116.383472,39.913395), new  BMap.Point(116.383323,39.913402), new  BMap.Point(116.382796,39.913403), new  BMap.Point(116.382747,39.913402), new  BMap.Point(116.38227,39.913394), new  BMap.Point(116.381594,39.913372), new  BMap.Point(116.381465,39.91337), new  BMap.Point(116.381445,39.913369), new  BMap.Point(116.381197,39.913365), new  BMap.Point(116.380779,39.913348), new  BMap.Point(116.38066,39.913346), new  BMap.Point(116.380491,39.913343), new  BMap.Point(116.380488,39.913341), new  BMap.Point(116.38014,39.913344), new  BMap.Point(116.379435,39.913342), new  BMap.Point(116.378848,39.913331), new  BMap.Point(116.378103,39.913307), new  BMap.Point(116.376722,39.913282), new  BMap.Point(116.374238,39.913227), new  BMap.Point(116.373105,39.913226), new  BMap.Point(116.37221,39.91321), new  BMap.Point(116.370004,39.913171), new  BMap.Point(116.369865,39.913169), new  BMap.Point(116.369706,39.913166), new  BMap.Point(116.369616,39.913175), new  BMap.Point(116.366474,39.913103), new  BMap.Point(116.366276,39.913109), new  BMap.Point(116.365997,39.913105), new  BMap.Point(116.365898,39.913104), new  BMap.Point(116.365639,39.91312), new  BMap.Point(116.364605,39.913094), new  BMap.Point(116.364127,39.913087), new  BMap.Point(116.364126,39.913086), new  BMap.Point(116.363917,39.913193), new  BMap.Point(116.363827,39.913262), new  BMap.Point(116.363797,39.913311), new  BMap.Point(116.363767,39.913391), new  BMap.Point(116.363767,39.913541), new  BMap.Point(116.363757,39.913771), new  BMap.Point(116.363757,39.913821), new  BMap.Point(116.363737,39.91432), new  BMap.Point(116.363687,39.914809), new  BMap.Point(116.363647,39.915039), new  BMap.Point(116.363617,39.915198), new  BMap.Point(116.363567,39.915377), new  BMap.Point(116.363527,39.915527), new  BMap.Point(116.363487,39.915676), new  BMap.Point(116.363438,39.915905), new  BMap.Point(116.363436,39.915905), new  BMap.Point(116.363327,39.916154), new  BMap.Point(116.363327,39.916154), new  BMap.Point(116.363356,39.916444), new  BMap.Point(116.363376,39.916694), new  BMap.Point(116.363504,39.917936), new  BMap.Point(116.363404,39.918974), new  BMap.Point(116.363384,39.919494), new  BMap.Point(116.363374,39.919673), new  BMap.Point(116.363374,39.919723), new  BMap.Point(116.363374,39.919763), new  BMap.Point(116.363374,39.919813), new  BMap.Point(116.363364,39.920293), new  BMap.Point(116.363364,39.920393), new  BMap.Point(116.363273,39.922131), new  BMap.Point(116.363263,39.922441), new  BMap.Point(116.363192,39.92405), new  BMap.Point(116.363183,39.92422), new  BMap.Point(116.363183,39.9243), new  BMap.Point(116.363173,39.92446), new  BMap.Point(116.363122,39.925599), new  BMap.Point(116.363112,39.925818), new  BMap.Point(116.363072,39.926798), new  BMap.Point(116.363052,39.927057), new  BMap.Point(116.363042,39.927277), new  BMap.Point(116.363002,39.928407), new  BMap.Point(116.362952,39.929326), new  BMap.Point(116.362952,39.929466), new  BMap.Point(116.362942,39.929486), new  BMap.Point(116.362842,39.931454), new  BMap.Point(116.362822,39.931694), new  BMap.Point(116.362822,39.931834), new  BMap.Point(116.362802,39.932393), new  BMap.Point(116.362772,39.933293), new  BMap.Point(116.362763,39.933313), new  BMap.Point(116.362683,39.934892), new  BMap.Point(116.362603,39.936531), new  BMap.Point(116.362544,39.93769), new  BMap.Point(116.362524,39.93804), new  BMap.Point(116.362514,39.938159), new  BMap.Point(116.362514,39.938209), new  BMap.Point(116.362514,39.938259), new  BMap.Point(116.362504,39.938509), new  BMap.Point(116.362465,39.939329), new  BMap.Point(116.362405,39.940878), new  BMap.Point(116.362376,39.941888), new  BMap.Point(116.362366,39.942108), new  BMap.Point(116.362366,39.942138), new  BMap.Point(116.362356,39.942398), new  BMap.Point(116.362337,39.942597), new  BMap.Point(116.362307,39.943287), new  BMap.Point(116.362277,39.943637), new  BMap.Point(116.362218,39.944746), new  BMap.Point(116.362219,39.946046), new  BMap.Point(116.362219,39.946196), new  BMap.Point(116.362219,39.946226), new  BMap.Point(116.362209,39.946276), new  BMap.Point(116.362209,39.946326), new  BMap.Point(116.362199,39.946436), new  BMap.Point(116.36218,39.946876), new  BMap.Point(116.36213,39.947595), new  BMap.Point(116.36227,39.948167), new  BMap.Point(116.36235,39.948339), new  BMap.Point(116.362509,39.948611), new  BMap.Point(116.362738,39.948914), new  BMap.Point(116.362928,39.949197), new  BMap.Point(116.363167,39.94943), new  BMap.Point(116.363416,39.949604), new  BMap.Point(116.363913,39.949851), new  BMap.Point(116.36469,39.950052), new  BMap.Point(116.365595,39.950366), new  BMap.Point(116.365794,39.950429), new  BMap.Point(116.366093,39.950524), new  BMap.Point(116.366242,39.950556), new  BMap.Point(116.367266,39.950863), new  BMap.Point(116.368162,39.951127), new  BMap.Point(116.371174,39.952029), new  BMap.Point(116.371324,39.952072), new  BMap.Point(116.372029,39.952315), new  BMap.Point(116.372417,39.952482), new  BMap.Point(116.37365,39.953024), new  BMap.Point(116.374435,39.953378), new  BMap.Point(116.374833,39.953536), new  BMap.Point(116.374962,39.953598), new  BMap.Point(116.375588,39.95386), new  BMap.Point(116.375585,39.95386), new  BMap.Point(116.375903,39.953895), new  BMap.Point(116.376171,39.95396), new  BMap.Point(116.376399,39.954035), new  BMap.Point(116.376648,39.954099), new  BMap.Point(116.376727,39.954131), new  BMap.Point(116.377026,39.954216), new  BMap.Point(116.377085,39.954237), new  BMap.Point(116.377354,39.954312), new  BMap.Point(116.377642,39.954358), new  BMap.Point(116.377821,39.954391), new  BMap.Point(116.378198,39.954458), new  BMap.Point(116.378377,39.954491), new  BMap.Point(116.378536,39.954504), new  BMap.Point(116.378586,39.954505), new  BMap.Point(116.378715,39.954517), new  BMap.Point(116.378924,39.954531), new  BMap.Point(116.379311,39.954558), new  BMap.Point(116.379848,39.954598), new  BMap.Point(116.379898,39.954599), new  BMap.Point(116.380037,39.954611), new  BMap.Point(116.380723,39.954664), new  BMap.Point(116.381458,39.954697), new  BMap.Point(116.381736,39.954712), new  BMap.Point(116.382432,39.954744), new  BMap.Point(116.38273,39.954759), new  BMap.Point(116.38279,39.95476), new  BMap.Point(116.383098,39.954795), new  BMap.Point(116.383456,39.954882), new  BMap.Point(116.383536,39.954903), new  BMap.Point(116.383576,39.954914), new  BMap.Point(116.383655,39.954935), new  BMap.Point(116.383695,39.954946), new  BMap.Point(116.383795,39.954987), new  BMap.Point(116.383854,39.955019), new  BMap.Point(116.383884,39.955029), new  BMap.Point(116.383954,39.95507), new  BMap.Point(116.384013,39.955121), new  BMap.Point(116.384063,39.955162), new  BMap.Point(116.384093,39.955183), new  BMap.Point(116.384153,39.955244), new  BMap.Point(116.384212,39.955315), new  BMap.Point(116.384242,39.955345), new  BMap.Point(116.384382,39.955518), new  BMap.Point(116.384441,39.955609), new  BMap.Point(116.384501,39.95568), new  BMap.Point(116.384581,39.955811), new  BMap.Point(116.38468,39.955953), new  BMap.Point(116.384849,39.956206), new  BMap.Point(116.384919,39.956317), new  BMap.Point(116.385059,39.956529), new  BMap.Point(116.385079,39.95657), new  BMap.Point(116.385347,39.956974), new  BMap.Point(116.385527,39.957267), new  BMap.Point(116.385576,39.957358), new  BMap.Point(116.385626,39.957459), new  BMap.Point(116.385676,39.95757), new  BMap.Point(116.385716,39.95768), new  BMap.Point(116.385776,39.957841), new  BMap.Point(116.385806,39.958002), new  BMap.Point(116.385836,39.958162), new  BMap.Point(116.385846,39.958293), new  BMap.Point(116.385846,39.958473), new  BMap.Point(116.385856,39.958623), new  BMap.Point(116.385856,39.958623), new  BMap.Point(116.385897,39.960014), new  BMap.Point(116.385898,39.960984), new  BMap.Point(116.385908,39.961295), new  BMap.Point(116.385929,39.961865), new  BMap.Point(116.385909,39.962295), new  BMap.Point(116.38588,39.963275), new  BMap.Point(116.385831,39.963644), new  BMap.Point(116.385782,39.964123), new  BMap.Point(116.385782,39.964143), new  BMap.Point(116.385842,39.964715), new  BMap.Point(116.385903,39.965356), new  BMap.Point(116.385983,39.966067), new  BMap.Point(116.386043,39.966538), new  BMap.Point(116.386053,39.966629), new  BMap.Point(116.386133,39.96726), new  BMap.Point(116.386414,39.969935), new  BMap.Point(116.386474,39.970407), new  BMap.Point(116.386685,39.9718), new  BMap.Point(116.386805,39.972673), new  BMap.Point(116.386935,39.973505), new  BMap.Point(116.386985,39.973796), new  BMap.Point(116.386984,39.973796), new  BMap.Point(116.387035,39.974166), new  BMap.Point(116.387055,39.974317), new  BMap.Point(116.387075,39.974427), new  BMap.Point(116.387095,39.974608), new  BMap.Point(116.387175,39.975129), new  BMap.Point(116.387185,39.975199), new  BMap.Point(116.387195,39.975309), new  BMap.Point(116.387285,39.975931), new  BMap.Point(116.387385,39.976803), new  BMap.Point(116.387626,39.978957), new  BMap.Point(116.387646,39.979127), new  BMap.Point(116.387656,39.979228), new  BMap.Point(116.387736,39.979829), new  BMap.Point(116.387866,39.981111), new  BMap.Point(116.387926,39.981592), new  BMap.Point(116.388026,39.982294), new  BMap.Point(116.388016,39.982684), new  BMap.Point(116.388007,39.982864), new  BMap.Point(116.387997,39.982953), new  BMap.Point(116.387997,39.983003), new  BMap.Point(116.387997,39.983054), new  BMap.Point(116.387987,39.983333), new  BMap.Point(116.387918,39.983782), new  BMap.Point(116.387819,39.984381), new  BMap.Point(116.3877,39.984779), new  BMap.Point(116.386975,39.987338), new  BMap.Point(116.386806,39.987906), new  BMap.Point(116.386776,39.988015), new  BMap.Point(116.386727,39.988195), new  BMap.Point(116.386518,39.988971), new  BMap.Point(116.386379,39.989529), new  BMap.Point(116.386221,39.990087), new  BMap.Point(116.386012,39.990823), new  BMap.Point(116.385943,39.991072), new  BMap.Point(116.385724,39.991819), new  BMap.Point(116.385376,39.992993), new  BMap.Point(116.385347,39.993123), new  BMap.Point(116.385237,39.993471), new  BMap.Point(116.385208,39.993591), new  BMap.Point(116.385168,39.99374), new  BMap.Point(116.385128,39.993889), new  BMap.Point(116.385109,39.993939), new  BMap.Point(116.385079,39.994048), new  BMap.Point(116.384622,39.995631), new  BMap.Point(116.384552,39.99591), new  BMap.Point(116.384254,39.996995), new  BMap.Point(116.384244,39.997034), new  BMap.Point(116.383936,39.997829), new  BMap.Point(116.383906,39.997919), new  BMap.Point(116.383687,39.998425), new  BMap.Point(116.383469,39.998841), new  BMap.Point(116.38327,39.999118), new  BMap.Point(116.382982,39.999513), new  BMap.Point(116.382534,40.000135), new  BMap.Point(116.382455,40.000253), new  BMap.Point(116.382157,40.000658), new  BMap.Point(116.382067,40.000777), new  BMap.Point(116.381491,40.001506), new  BMap.Point(116.381023,40.002088), new  BMap.Point(116.380516,40.002739), new  BMap.Point(116.380188,40.003163), new  BMap.Point(116.379612,40.003902), new  BMap.Point(116.379234,40.004385), new  BMap.Point(116.378578,40.005213), new  BMap.Point(116.377942,40.006032), new  BMap.Point(116.377524,40.006564), new  BMap.Point(116.376937,40.007333), new  BMap.Point(116.376848,40.007431), new  BMap.Point(116.376789,40.00752), new  BMap.Point(116.376749,40.007559), new  BMap.Point(116.376739,40.007589), new  BMap.Point(116.376431,40.007983), new  BMap.Point(116.376043,40.008486), new  BMap.Point(116.374452,40.010616), new  BMap.Point(116.373895,40.011406), new  BMap.Point(116.373606,40.011791), new  BMap.Point(116.372303,40.013577), new  BMap.Point(116.371706,40.014386), new  BMap.Point(116.371617,40.014514), new  BMap.Point(116.37106,40.015244), new  BMap.Point(116.37097,40.015373), new  BMap.Point(116.370542,40.015935), new  BMap.Point(116.369577,40.017208), new  BMap.Point(116.369259,40.017652), new  BMap.Point(116.368711,40.018443), new  BMap.Point(116.368303,40.018946), new  BMap.Point(116.367557,40.019883), new  BMap.Point(116.36687,40.020752), new  BMap.Point(116.366183,40.02162), new  BMap.Point(116.364589,40.023695), new  BMap.Point(116.363912,40.024585), new  BMap.Point(116.363334,40.025317), new  BMap.Point(116.362975,40.025762), new  BMap.Point(116.362816,40.025959), new  BMap.Point(116.362228,40.026711), new  BMap.Point(116.361869,40.027156), new  BMap.Point(116.36164,40.027463), new  BMap.Point(116.361421,40.02776), new  BMap.Point(116.361401,40.02779), new  BMap.Point(116.361311,40.027899), new  BMap.Point(116.361304,40.027899), new  BMap.Point(116.361274,40.027948), new  BMap.Point(116.361175,40.028107), new  BMap.Point(116.361085,40.028236), new  BMap.Point(116.360995,40.028385), new  BMap.Point(116.360945,40.028474), new  BMap.Point(116.360916,40.028524), new  BMap.Point(116.360806,40.028712), new  BMap.Point(116.360706,40.028881), new  BMap.Point(116.360677,40.028941), new  BMap.Point(116.360667,40.028961), new  BMap.Point(116.360637,40.02903), new  BMap.Point(116.360597,40.02912), new  BMap.Point(116.360557,40.029199), new  BMap.Point(116.360547,40.029219), new  BMap.Point(116.360538,40.029259), new  BMap.Point(116.360518,40.029299), new  BMap.Point(116.360508,40.029338), new  BMap.Point(116.360508,40.029388), new  BMap.Point(116.360508,40.029438), new  BMap.Point(116.360508,40.029488), new  BMap.Point(116.360518,40.029539), new  BMap.Point(116.360528,40.029589), new  BMap.Point(116.360547,40.029639), new  BMap.Point(116.360577,40.029679), new  BMap.Point(116.360607,40.02972), new  BMap.Point(116.360647,40.02976), new  BMap.Point(116.360687,40.029791), new  BMap.Point(116.360726,40.029821), new  BMap.Point(116.360766,40.029841), new  BMap.Point(116.360806,40.029862), new  BMap.Point(116.360846,40.029872), new  BMap.Point(116.360896,40.029883), new  BMap.Point(116.360945,40.029894), new  BMap.Point(116.360995,40.029904), new  BMap.Point(116.361055,40.029905), new  BMap.Point(116.361115,40.029906), new  BMap.Point(116.361174,40.029907), new  BMap.Point(116.361234,40.029887), new  BMap.Point(116.361294,40.029878), new  BMap.Point(116.361354,40.029859), new  BMap.Point(116.361393,40.02984), new  BMap.Point(116.361453,40.029811), new  BMap.Point(116.361493,40.029781), new  BMap.Point(116.361533,40.029742), new  BMap.Point(116.361573,40.029692), new  BMap.Point(116.361603,40.029643), new  BMap.Point(116.361633,40.029593), new  BMap.Point(116.361643,40.029544), new  BMap.Point(116.361643,40.029504), new  BMap.Point(116.361643,40.029464), new  BMap.Point(116.361633,40.029424), new  BMap.Point(116.361623,40.029374), new  BMap.Point(116.361603,40.029323), new  BMap.Point(116.361574,40.029273), new  BMap.Point(116.361544,40.029233), new  BMap.Point(116.361504,40.029202), new  BMap.Point(116.361474,40.029172), new  BMap.Point(116.361425,40.029141), new  BMap.Point(116.361385,40.029101), new  BMap.Point(116.361325,40.02906), new  BMap.Point(116.361266,40.02904), new  BMap.Point(116.361226,40.029009), new  BMap.Point(116.361186,40.028989), new  BMap.Point(116.361147,40.028968), new  BMap.Point(116.361107,40.028948), new  BMap.Point(116.361104,40.028946), new  BMap.Point(116.360765,40.028942), new  BMap.Point(116.360676,40.028941), new  BMap.Point(116.360477,40.028939), new  BMap.Point(116.360228,40.028936), new  BMap.Point(116.359521,40.028937), new  BMap.Point(116.359292,40.028945), new  BMap.Point(116.358943,40.028951), new  BMap.Point(116.358515,40.028946), new  BMap.Point(116.358495,40.028946), new  BMap.Point(116.357638,40.028947), new  BMap.Point(116.357499,40.028946), new  BMap.Point(116.357011,40.028931), new  BMap.Point(116.356801,40.028929), new  BMap.Point(116.356792,40.028929), new  BMap.Point(116.356632,40.028928), new  BMap.Point(116.355356,40.028937), new  BMap.Point(116.355037,40.028934), new  BMap.Point(116.354628,40.028941), new  BMap.Point(116.354219,40.028938), new  BMap.Point(116.353571,40.028934), new  BMap.Point(116.353062,40.028941), new  BMap.Point(116.352872,40.02894), new  BMap.Point(116.351874,40.028934), new  BMap.Point(116.350277,40.028937), new  BMap.Point(116.347059,40.028961), new  BMap.Point(116.346519,40.028971), new  BMap.Point(116.343748,40.028925), new  BMap.Point(116.343718,40.028925), new  BMap.Point(116.343608,40.028925), new  BMap.Point(116.342968,40.028888), new  BMap.Point(116.341516,40.028704), new  BMap.Point(116.340915,40.028537), new  BMap.Point(116.340184,40.028331), new  BMap.Point(116.337588,40.02754), new  BMap.Point(116.337585,40.027539), new  BMap.Point(116.337244,40.027532), new  BMap.Point(116.336974,40.027464), new  BMap.Point(116.336522,40.027388), new  BMap.Point(116.336201,40.027321), new  BMap.Point(116.33576,40.027336), new  BMap.Point(116.335309,40.02738), new  BMap.Point(116.334907,40.027494), new  BMap.Point(116.334445,40.027659), new  BMap.Point(116.334405,40.027679), new  BMap.Point(116.333823,40.027946), new  BMap.Point(116.333311,40.028252), new  BMap.Point(116.332839,40.028617), new  BMap.Point(116.331713,40.029801), new  BMap.Point(116.331711,40.0298), new  BMap.Point(116.330726,40.031463), new  BMap.Point(116.330585,40.031744), new  BMap.Point(116.330071,40.032911), new  BMap.Point(116.330021,40.033112), new  BMap.Point(116.329437,40.035059), new  BMap.Point(116.329245,40.035672), new  BMap.Point(116.329165,40.035913), new  BMap.Point(116.329084,40.036174), new  BMap.Point(116.328984,40.036465), new  BMap.Point(116.328943,40.036576), new  BMap.Point(116.328802,40.037018), new  BMap.Point(116.328209,40.038366), new  BMap.Point(116.327716,40.039103), new  BMap.Point(116.32685,40.040136), new  BMap.Point(116.326659,40.040299), new  BMap.Point(116.325231,40.041552), new  BMap.Point(116.323168,40.043356), new  BMap.Point(116.323047,40.043468), new  BMap.Point(116.323017,40.043489), new  BMap.Point(116.322937,40.04357), new  BMap.Point(116.322806,40.043682), new  BMap.Point(116.322756,40.043733), new  BMap.Point(116.322715,40.043764), new  BMap.Point(116.322243,40.044202), new  BMap.Point(116.322233,40.044222), new  BMap.Point(116.321719,40.044821), new  BMap.Point(116.321578,40.044983), new  BMap.Point(116.320773,40.046077), new  BMap.Point(116.320451,40.046673), new  BMap.Point(116.319867,40.047743), new  BMap.Point(116.319122,40.049116), new  BMap.Point(116.31878,40.049552), new  BMap.Point(116.317048,40.051744), new  BMap.Point(116.316032,40.053022), new  BMap.Point(116.315639,40.053519), new  BMap.Point(116.313536,40.056347), new  BMap.Point(116.313365,40.05658), new  BMap.Point(116.313265,40.056712), new  BMap.Point(116.313215,40.056783), new  BMap.Point(116.313064,40.056985), new  BMap.Point(116.31242,40.058037), new  BMap.Point(116.311555,40.059442), new  BMap.Point(116.311535,40.059473), new  BMap.Point(116.311123,40.06015), new  BMap.Point(116.311118,40.06015), new  BMap.Point(116.311119,40.06048), new  BMap.Point(116.310968,40.060772), new  BMap.Point(116.310385,40.061943), new  BMap.Point(116.310334,40.062044), new  BMap.Point(116.310103,40.062488), new  BMap.Point(116.310063,40.062568), new  BMap.Point(116.310033,40.062629), new  BMap.Point(116.309309,40.064021), new  BMap.Point(116.308897,40.064488), new  BMap.Point(116.308555,40.064634), new  BMap.Point(116.308375,40.064707), new  BMap.Point(116.308244,40.064759), new  BMap.Point(116.307963,40.064834), new  BMap.Point(116.30756,40.064791), new  BMap.Point(116.306866,40.064622), new  BMap.Point(116.304856,40.064163), new  BMap.Point(116.303499,40.063863), new  BMap.Point(116.302966,40.06374), new  BMap.Point(116.302856,40.063682), new  BMap.Point(116.302854,40.063681), new  BMap.Point(116.302995,40.063199), new  BMap.Point(116.303979,40.060475), new  BMap.Point(116.303989,40.060395), new  BMap.Point(116.304079,40.060064), new  BMap.Point(116.30432,40.05918), new  BMap.Point(116.304501,40.058558), new  BMap.Point(116.304521,40.058518), new  BMap.Point(116.304662,40.058015), new  BMap.Point(116.304933,40.057151), new  BMap.Point(116.305194,40.056358), new  BMap.Point(116.305235,40.056207), new  BMap.Point(116.305325,40.055946), new  BMap.Point(116.305566,40.055212), new  BMap.Point(116.305647,40.054951), new  BMap.Point(116.305747,40.05494), new  BMap.Point(116.305717,40.05502), new  BMap.Point(116.305717,40.055019), new  BMap.Point(116.305647,40.05523), new  BMap.Point(116.305416,40.055964), new  BMap.Point(116.305295,40.056375), new  BMap.Point(116.305034,40.057169), new  BMap.Point(116.304773,40.058033), new  BMap.Point(116.304772,40.058033), new  BMap.Point(116.306491,40.058347), new  BMap.Point(116.306492,40.058347), new  BMap.Point(116.306914,40.05729), new  BMap.Point(116.306954,40.05717), new  BMap.Point(116.306954,40.05717), new  BMap.Point(116.306974,40.057169)];
             //    map.addOverlay(new BMap.Polyline(arrPois, {strokeColor: '#111'})); //不画线
                 map.setViewport(arrPois);
                    marker= new  BMap.Marker(arrPois[0],{
                       icon  :  new  BMap.Icon( 'http://developer.baidu.com/map/jsdemo/img/car.png' new  BMap.Size(52,26),{anchor :  new  BMap.Size(27, 13)})
                    });
       var  label =  new  BMap.Label( "粤A30780" ,{offset: new  BMap.Size(0,-30)});
       label.setStyle({border: "1px solid rgb(204, 204, 204)" ,color:  "rgb(0, 0, 0)" ,borderRadius: "10px" ,padding: "5px" ,background: "rgb(255, 255, 255)" ,});
                 marker.setLabel(label);
                 
       map.addOverlay(marker);
      BMapLib.LuShu.prototype._move= function (initPos,targetPos,effect) {
             var  pointsArr=[initPos,targetPos];   //点数组
             var  me =  this ,
                 //当前的帧数
                 currentCount = 0,
                 //步长,米/秒
                 timer = 10,
                 step =  this ._opts.speed / (1000 / timer),
                 //初始坐标
                 init_pos =  this ._projection.lngLatToPoint(initPos),
                 //获取结束点的(x,y)坐标
                 target_pos =  this ._projection.lngLatToPoint(targetPos),
                 //总的步长
                 count = Math.round(me._getDistance(init_pos, target_pos) / step);
                  //显示折线 syj201607191107
             this ._map.addOverlay( new  BMap.Polyline(pointsArr, { 
                 strokeColor :  "#111"
                 strokeWeight : 5, 
                 strokeOpacity : 0.5 
             }));  // 画线 
             //如果小于1直接移动到下一点
             if  (count < 1) {
                 me._moveNext(++me.i);
                 return ;
             }
             me._intervalFlag = setInterval( function () {
             //两点之间当前帧数大于总帧数的时候,则说明已经完成移动
                 if  (currentCount >= count) {
                     clearInterval(me._intervalFlag);
                     //移动的点已经超过总的长度
                     if (me.i > me._path.length){
                         return ;
                     }
                     //运行下一个点
                     me._moveNext(++me.i);
                 } else  {
                         currentCount++;
                         var  x = effect(init_pos.x, target_pos.x, currentCount, count),
                             y = effect(init_pos.y, target_pos.y, currentCount, count),
                             pos = me._projection.pointToLngLat( new  BMap.Pixel(x, y));
                         //设置marker
                         if (currentCount == 1){
                             var  proPos =  null ;
                             if (me.i - 1 >= 0){
                                 proPos = me._path[me.i - 1];
                             }
                             if (me._opts.enableRotation ==  true ){
                                  me.setRotation(proPos,initPos,targetPos);
                             }
                             if (me._opts.autoView){
                                 if (!me._map.getBounds().containsPoint(pos)){
                                     me._map.setCenter(pos);
                                 }  
                             }
                         }
                         //正在移动
                         me._marker.setPosition(pos);
                         //设置自定义overlay的位置
                         me._setInfoWin(pos);  
                     }
             },timer);
         };
                 lushu =  new  BMapLib.LuShu(map,arrPois,{
                 defaultContent: "粤A30780" , //"从天安门到百度大厦"
                 autoView: true , //是否开启自动视野调整,如果开启那么路书在运动过程中会根据视野自动调整
                 icon  :  new  BMap.Icon( 'http://developer.baidu.com/map/jsdemo/img/car.png' new  BMap.Size(52,26),{anchor :  new  BMap.Size(27, 13)}),
                 speed: 4500,
                 enableRotation: true , //是否设置marker随着道路的走向进行旋转
                    landmarkPois:[
                    {lng:116.306954,lat:40.05718,html: '加油站' ,pauseTime:2}
                   ]
                    
                 }); 
         
       marker.addEventListener( "click" , function (){
         marker.enableMassClear();    //设置后可以隐藏改点的覆盖物
         marker.hide();
         lushu.start();
         //map.clearOverlays();  //清除所有覆盖物
       });
             //}
//        }
   //  });
     //drv.search('天安门', '百度大厦');
      // lushu.start();
      // lushu.pause();
     //绑定事件
     $( "run" ).onclick =  function (){
       marker.enableMassClear();  //设置后可以隐藏改点的覆盖物
       marker.hide();
       lushu.start();
      // map.clearOverlays();    //清除所有覆盖物
     }
     $( "stop" ).onclick =  function (){
         lushu.stop();
     }
     $( "pause" ).onclick =  function (){
         lushu.pause();
     }
     $( "hide" ).onclick =  function (){
         lushu.hideInfoWindow();
     }
     $( "show" ).onclick =  function (){
         lushu.showInfoWindow();
     }
     function  $(element){
         return  document.getElementById(element);
     }
</script>
</body>
</html>

  其中,arrPois的点是自定义点,非常适合做轨迹回放用.

  注意:landmarkPois 必须要定义,如果没有要显示的点话,就写成[],不能为空,否则会无法运行.

  实现边走边画线,首先将百度路书中的画线代码注释掉,然后重写_move方法,参见代码中的重写_move方法.基于此可以实现更加复杂的各种附加信息.

  其效果如下图:

  

  也算是给后人留点启发吧

import tkinter as tk from tkinter import ttk, messagebox, filedialog, colorchooser import math import time import csv import os import random from PIL import Image, ImageTk import numpy as np class RobotArm: """工业机器人模型""" def __init__(self): # 机器人参数 self.base_x = 400 # 基座X坐标 self.base_y = 550 # 基座Y坐标 # 关节长度 self.joint1_length = 120 # 第一关节长度 self.joint2_length = 100 # 第二关节长度 self.joint3_length = 80 # 第三关节长度 self.joint4_length = 60 # 第四关节长度 self.end_effector_length = 40 # 末端执行器长度 # 关节角度 (单位: 度) self.joint1_angle = 45 # 第一关节角度 (0-180) self.joint2_angle = -30 # 第二关节角度 (-90-90) self.joint3_angle = 20 # 第三关节角度 (-90-90) self.joint4_angle = -10 # 第四关节角度 (-45-45) # 末端位置 self.x = 0 self.y = 0 # 绘图状态 self.pen_down = False self.pen_color = "#0000FF" # 蓝色 self.pen_width = 3 self.pen_style = "solid" self.drawing_path = [] # 绘图径 # 障碍物 self.obstacles = [] # 更新位置 self.update_position() def update_position(self): """根据关节角度更新末端位置""" # 将角度转换为弧度 j1_rad = math.radians(self.joint1_angle) j2_rad = math.radians(self.joint2_angle + self.joint1_angle) j3_rad = math.radians(self.joint3_angle + self.joint2_angle + self.joint1_angle) j4_rad = math.radians(self.joint4_angle + self.joint3_angle + self.joint2_angle + self.joint1_angle) # 计算关节位置 self.joint1_x = self.base_x + self.joint1_length * math.cos(j1_rad) self.joint1_y = self.base_y - self.joint1_length * math.sin(j1_rad) self.joint2_x = self.joint1_x + self.joint2_length * math.cos(j2_rad) self.joint2_y = self.joint1_y - self.joint2_length * math.sin(j2_rad) self.joint3_x = self.joint2_x + self.joint3_length * math.cos(j3_rad) self.joint3_y = self.joint2_y - self.joint3_length * math.sin(j3_rad) self.joint4_x = self.joint3_x + self.joint4_length * math.cos(j4_rad) self.joint4_y = self.joint3_y - self.joint4_length * math.sin(j4_rad) # 计算末端位置 self.x = self.joint4_x + self.end_effector_length * math.cos(j4_rad) self.y = self.joint4_y - self.end_effector_length * math.sin(j4_rad) def set_angles(self, j1, j2, j3, j4): """设置关节角度""" self.joint1_angle = max(0, min(180, j1)) self.joint2_angle = max(-90, min(90, j2)) self.joint3_angle = max(-90, min(90, j3)) self.joint4_angle = max(-45, min(45, j4)) self.update_position() def move_to(self, x, y, avoid_obstacles=False): """移动机械臂末端到指定位置(逆运动学)""" # 简化的逆运动学计算 dx = x - self.base_x dy = self.base_y - y # 计算到目标点的距离 distance = math.sqrt(dx**2 + dy**2) # 检查是否在可到达范围内 max_reach = self.joint1_length + self.joint2_length + self.joint3_length + self.joint4_length if distance > max_reach: # 如果超出范围,移动到最大范围处 scale = max_reach / distance x = self.base_x + dx * scale y = self.base_y - dy * scale distance = max_reach dx = x - self.base_x dy = self.base_y - y # 计算第一关节角度 j1_angle = math.degrees(math.atan2(dy, dx)) # 计算其他关节角度 (简化计算) # 在实际应用中,这里应该使用更复杂的逆运动学算法 # 这里使用简化方法,保持其他关节角度相对固定 j2_angle = -30 j3_angle = 20 j4_angle = -10 # 设置角度 self.set_angles(j1_angle, j2_angle, j3_angle, j4_angle) def start_drawing(self): """开始绘图""" self.pen_down = True # 记录起始点 self.drawing_path.append({ "x": self.x, "y": self.y, "color": self.pen_color, "width": self.pen_width, "style": self.pen_style, "points": [(self.x, self.y)] }) def add_drawing_point(self, x, y): """添加绘图点""" if self.pen_down and self.drawing_path: self.drawing_path[-1]["points"].append((x, y)) def stop_drawing(self): """停止绘图""" self.pen_down = False def add_obstacle(self, x1, y1, x2, y2): """添加障碍物""" self.obstacles.append((x1, y1, x2, y2)) def clear_obstacles(self): """清除所有障碍物""" self.obstacles = [] def is_collision(self, x, y): """检测给定点是否与障碍物碰撞""" for obstacle in self.obstacles: x1, y1, x2, y2 = obstacle if x1 <= x <= x2 and y1 <= y <= y2: return True return False def avoid_obstacles(self, start_x, start_y, target_x, target_y): """避障径规划""" # 简单避障算法 - 绕行矩形障碍物 if not self.obstacles: return [(target_x, target_y)] # 检查径是否穿过障碍物 for obstacle in self.obstacles: x1, y1, x2, y2 = obstacle if self.line_intersects_rect(start_x, start_y, target_x, target_y, x1, y1, x2, y2): # 计算绕行点 # 尝试从上方绕行 waypoint_x = (start_x + target_x) / 2 waypoint_y = min(y1, y2) - 20 # 检查绕行点是否在障碍物内 if not self.is_collision(waypoint_x, waypoint_y): return [(waypoint_x, waypoint_y), (target_x, target_y)] # 尝试从下方绕行 waypoint_y = max(y1, y2) + 20 if not self.is_collision(waypoint_x, waypoint_y): return [(waypoint_x, waypoint_y), (target_x, target_y)] # 尝试从左方绕行 waypoint_x = min(x1, x2) - 20 waypoint_y = (start_y + target_y) / 2 if not self.is_collision(waypoint_x, waypoint_y): return [(waypoint_x, waypoint_y), (target_x, target_y)] # 尝试从右方绕行 waypoint_x = max(x1, x2) + 20 if not self.is_collision(waypoint_x, waypoint_y): return [(waypoint_x, waypoint_y), (target_x, target_y)] # 如果没有障碍物阻挡,直接返回目标点 return [(target_x, target_y)] def line_intersects_rect(self, x1, y1, x2, y2, rx1, ry1, rx2, ry2): """检查线段是否与矩形相交""" # 确保矩形坐标有序 rx_min, rx_max = min(rx1, rx2), max(rx1, rx2) ry_min, ry_max = min(ry1, ry2), max(ry1, ry2) # 检查线段是否在矩形外 if max(x1, x2) < rx_min or min(x1, x2) > rx_max or max(y1, y2) < ry_min or min(y1, y2) > ry_max: return False # 检查线段是否与矩形相交 # 左缘 if self.line_intersects_line(x1, y1, x2, y2, rx_min, ry_min, rx_min, ry_max): return True # 右缘 if self.line_intersects_line(x1, y1, x2, y2, rx_max, ry_min, rx_max, ry_max): return True # 上缘 if self.line_intersects_line(x1, y1, x2, y2, rx_min, ry_min, rx_max, ry_min): return True # 下缘 if self.line_intersects_line(x1, y1, x2, y2, rx_min, ry_max, rx_max, ry_max): return True return False def line_intersects_line(self, x1, y1, x2, y2, x3, y3, x4, y4): """检查两条线段是否相交""" # 计算分母 denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) if denom == 0: return False # 平行线 ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom # 检查交点是否在线段上 if 0 <= ua <= 1 and 0 <= ub <= 1: return True return False class SimulationFrame(ttk.Frame): """主仿真界面 - 工业机器人绘图功能""" def __init__(self, parent): super().__init__(parent) self.robot = RobotArm() self.modes = ["手动绘图", "自动径", "远程控制"] self.current_mode = tk.IntVar(value=0) # 默认手动模式 self.playback_trajectory = [] self.is_playing = False self.recording = False self.playback_index = 0 self.dragging = False self.dragging_arm = False self.drag_start_x = 0 self.drag_start_y = 0 self.manual_drawing = False self.path_types = ["圆形", "方形", "三角形", "五角星", "螺旋线", "心形", "正弦波", "字母A", "字母B", "数字8", "星形", "花朵", "迷宫", "螺旋方", "自定义"] self.selected_path = tk.StringVar(value="圆形") self.pen_styles = ["solid", "dash", "dot", "dashdot"] self.create_widgets() self.animate() # 添加一些初始障碍物 self.robot.add_obstacle(300, 300, 400, 400) self.robot.add_obstacle(500, 200, 600, 300) def create_widgets(self): """创建界面组件""" # 创建主框架 main_frame = ttk.Frame(self) main_frame.pack(fill="both", expand=True, padx=10, pady=10) # 左侧面板 - 状态和画布 left_frame = ttk.Frame(main_frame) left_frame.pack(side="left", fill="both", expand=True) # 状态栏 status_frame = ttk.Frame(left_frame) status_frame.pack(fill="x", pady=(0, 10)) self.status_var = tk.StringVar(value="系统就绪 - 手动绘图模式") status_label = ttk.Label(status_frame, textvariable=self.status_var, foreground="green", font=("Arial", 10, "bold")) status_label.pack(side="left") # 坐标显示 coord_frame = ttk.Frame(status_frame) coord_frame.pack(side="right") self.coord_var = tk.StringVar(value="末端坐标: (0.0, 0.0)") ttk.Label(coord_frame, textvariable=self.coord_var, font=("Consolas", 9)).pack(side="left", padx=(10, 5)) # 关节角度显示 self.angle_var = tk.StringVar(value="关节角度: J1 0°, J2 0°, J3 0°, J4 0°") ttk.Label(coord_frame, textvariable=self.angle_var, font=("Consolas", 9)).pack(side="left", padx=5) # 画布 canvas_frame = ttk.Frame(left_frame) canvas_frame.pack(fill="both", expand=True) # 增大画布尺寸 self.canvas = tk.Canvas(canvas_frame, width=800, height=600, bg="white", highlightthickness=1, highlightbackground="#CCCCCC") self.canvas.pack(fill="both", expand=True, pady=(0, 10)) # 绑定鼠标事件 self.canvas.bind("<Button-1>", self.canvas_click) self.canvas.bind("<B1-Motion>", self.canvas_drag) self.canvas.bind("<ButtonRelease-1>", self.canvas_release) # 绘制工作区 self.draw_workspace() # 操作日志 log_frame = ttk.LabelFrame(left_frame, text="操作日志") log_frame.pack(fill="both", expand=True) self.log_text = tk.Text(log_frame, height=5, state="disabled") scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview) self.log_text.config(yscrollcommand=scrollbar.set) self.log_text.pack(side="left", fill="both", expand=True, padx=5, pady=5) scrollbar.pack(side="right", fill="y", padx=(0, 5), pady=5) self.log("系统初始化完成") # 右侧控制面板 control_frame = ttk.LabelFrame(main_frame, text="控制面板") control_frame.pack(side="right", fill="y", padx=(10, 0)) # 使用Notebook组织控制面板 notebook = ttk.Notebook(control_frame) notebook.pack(fill="both", expand=True, padx=5, pady=5) # 模式控制标签页 mode_tab = ttk.Frame(notebook) notebook.add(mode_tab, text="模式控制") # 径控制标签页 path_tab = ttk.Frame(notebook) notebook.add(path_tab, text="径控制") # 笔头设置标签页 pen_tab = ttk.Frame(notebook) notebook.add(pen_tab, text="笔头设置") # 轨迹控制标签页 traj_tab = ttk.Frame(notebook) notebook.add(traj_tab, text="轨迹控制") # 模式选择 mode_frame = ttk.LabelFrame(mode_tab, text="操作模式") mode_frame.pack(fill="x", padx=10, pady=5) for i, mode in enumerate(self.modes): rb = ttk.Radiobutton(mode_frame, text=mode, variable=self.current_mode, value=i, command=self.mode_changed) rb.pack(side="top", padx=10, pady=2, fill="x") # 径设置 path_frame = ttk.LabelFrame(path_tab, text="径设置") path_frame.pack(fill="x", padx=10, pady=5) # 径类型选择 ttk.Label(path_frame, text="径类型:").pack(side="top", anchor="w", padx=(5, 0)) path_combo = ttk.Combobox(path_frame, textvariable=self.selected_path, values=self.path_types, width=15) path_combo.pack(fill="x", padx=5, pady=2) path_combo.bind("<<ComboboxSelected>>", self.path_changed) # 径控制按钮 path_btn_frame = ttk.Frame(path_frame) path_btn_frame.pack(fill="x", pady=5) self.start_path_btn = ttk.Button(path_btn_frame, text="开始径", command=self.start_path) self.start_path_btn.pack(side="left", padx=2, fill="x", expand=True) stop_path_btn = ttk.Button(path_btn_frame, text="停止径", command=self.stop_path) stop_path_btn.pack(side="left", padx=2, fill="x", expand=True) # 径参数 path_param_frame = ttk.Frame(path_frame) path_param_frame.pack(fill="x", pady=2) ttk.Label(path_param_frame, text="尺寸:").pack(side="left", padx=(5, 0)) self.path_size_var = tk.IntVar(value=150) size_slider = ttk.Scale(path_param_frame, from_=50, to=300, orient="horizontal", variable=self.path_size_var, length=120) size_slider.pack(side="left", padx=5, fill="x", expand=True) size_value = ttk.Label(path_param_frame, textvariable=self.path_size_var, width=3) size_value.pack(side="left", padx=(0, 5)) # 笔头设置 pen_frame = ttk.LabelFrame(pen_tab, text="笔头设置") pen_frame.pack(fill="x", padx=10, pady=5) # 笔头颜色 color_frame = ttk.Frame(pen_frame) color_frame.pack(fill="x", pady=2) ttk.Label(color_frame, text="颜色:").pack(side="left", padx=(5, 0)) self.color_btn = ttk.Button(color_frame, text="选择颜色", command=self.choose_color, width=10) self.color_btn.pack(side="left", padx=5) self.color_preview = tk.Canvas(color_frame, width=30, height=20, bg=self.robot.pen_color) self.color_preview.pack(side="left", padx=5) # 笔头粗细 width_frame = ttk.Frame(pen_frame) width_frame.pack(fill="x", pady=2) ttk.Label(width_frame, text="粗细:").pack(side="left", padx=(5, 0)) self.width_var = tk.IntVar(value=self.robot.pen_width) width_slider = ttk.Scale(width_frame, from_=1, to=10, orient="horizontal", variable=self.width_var, command=self.update_pen_width, length=120) width_slider.pack(side="left", padx=5, fill="x", expand=True) width_value = ttk.Label(width_frame, textvariable=self.width_var, width=3) width_value.pack(side="left", padx=(0, 5)) # 笔头样式 style_frame = ttk.Frame(pen_frame) style_frame.pack(fill="x", pady=2) ttk.Label(style_frame, text="样式:").pack(side="left", padx=(5, 0)) self.style_var = tk.StringVar(value=self.robot.pen_style) style_combo = ttk.Combobox(style_frame, textvariable=self.style_var, values=self.pen_styles, width=10) style_combo.pack(side="left", padx=5, fill="x", expand=True) style_combo.bind("<<ComboboxSelected>>", self.update_pen_style) # 控制按钮 btn_frame = ttk.LabelFrame(pen_tab, text="机械臂控制") btn_frame.pack(fill="x", padx=10, pady=5) button_row1 = ttk.Frame(btn_frame) button_row1.pack(fill="x", pady=2) self.start_btn = ttk.Button(button_row1, text="开始绘图", command=self.start_drawing) self.start_btn.pack(side="left", padx=2, pady=2, fill="x", expand=True) self.stop_btn = ttk.Button(button_row1, text="停止绘图", command=self.stop_drawing) self.stop_btn.pack(side="left", padx=2, pady=2, fill="x", expand=True) button_row2 = ttk.Frame(btn_frame) button_row2.pack(fill="x", pady=2) self.home_btn = ttk.Button(button_row2, text="归位", command=self.go_home) self.home_btn.pack(side="left", padx=2, pady=2, fill="x", expand=True) self.clear_btn = ttk.Button(button_row2, text="清除画布", command=self.clear_canvas) self.clear_btn.pack(side="left", padx=2, pady=2, fill="x", expand=True) # 速度控制 speed_frame = ttk.LabelFrame(pen_tab, text="速度控制") speed_frame.pack(fill="x", padx=10, pady=5) ttk.Label(speed_frame, text="速度:").pack(side="left", padx=(5, 0)) self.speed_var = tk.DoubleVar(value=1.0) speed_slider = ttk.Scale(speed_frame, from_=0.1, to=2.0, orient="horizontal", variable=self.speed_var, command=self.update_speed, length=150) speed_slider.pack(side="left", padx=5, fill="x", expand=True) speed_value = ttk.Label(speed_frame, textvariable=self.speed_var, width=4) speed_value.pack(side="left", padx=(0, 5)) # 轨迹控制 traj_frame = ttk.LabelFrame(traj_tab, text="轨迹控制") traj_frame.pack(fill="x", padx=10, pady=5) self.record_btn = ttk.Button(traj_frame, text="开始记录轨迹", command=self.toggle_record_trajectory) self.record_btn.pack(fill="x", padx=5, pady=2) self.playback_btn = ttk.Button(traj_frame, text="回放轨迹", command=self.playback_trajectory) self.playback_btn.pack(fill="x", padx=5, pady=2) self.save_traj_btn = ttk.Button(traj_frame, text="保存轨迹", command=self.save_trajectory) self.save_traj_btn.pack(fill="x", padx=5, pady=2) self.clear_traj_btn = ttk.Button(traj_frame, text="清除轨迹", command=self.clear_trajectory) self.clear_traj_btn.pack(fill="x", padx=5, pady=2) # 保存和加载绘图 save_frame = ttk.LabelFrame(traj_tab, text="绘图操作") save_frame.pack(fill="x", padx=10, pady=5) save_btn = ttk.Button(save_frame, text="保存绘图", command=self.save_drawing) save_btn.pack(fill="x", padx=5, pady=2) load_btn = ttk.Button(save_frame, text="加载绘图", command=self.load_drawing) load_btn.pack(fill="x", padx=5, pady=2) # 避障控制 obstacle_frame = ttk.LabelFrame(traj_tab, text="避障设置") obstacle_frame.pack(fill="x", padx=10, pady=5) add_obstacle_btn = ttk.Button(obstacle_frame, text="添加障碍物", command=self.add_obstacle) add_obstacle_btn.pack(fill="x", padx=5, pady=2) clear_obstacle_btn = ttk.Button(obstacle_frame, text="清除障碍物", command=self.clear_obstacles) clear_obstacle_btn.pack(fill="x", padx=5, pady=2) self.avoid_var = tk.BooleanVar(value=True) avoid_check = ttk.Checkbutton(obstacle_frame, text="启用自动避障", variable=self.avoid_var) avoid_check.pack(fill="x", padx=5, pady=2) def mode_changed(self): """模式改变事件处理""" mode_index = self.current_mode.get() self.status_var.set(f"系统就绪 - {self.modes[mode_index]}模式") self.log(f"切换到{self.modes[mode_index]}模式") def path_changed(self, event): """径类型改变事件处理""" self.log(f"已选择径类型: {self.selected_path.get()}") def choose_color(self): """选择笔头颜色""" color = colorchooser.askcolor(title="选择笔头颜色", initialcolor=self.robot.pen_color) if color[1]: self.robot.pen_color = color[1] self.color_preview.config(bg=color[1]) self.log(f"笔头颜色设置为: {color[1]}") def update_pen_width(self, value): """更新笔头粗细""" self.robot.pen_width = int(float(value)) self.log(f"笔头粗细设置为: {self.robot.pen_width}像素") def update_pen_style(self, event): """更新笔头样式""" self.robot.pen_style = self.style_var.get() self.log(f"笔头样式设置为: {self.robot.pen_style}") def start_drawing(self): """开始绘图""" self.robot.start_drawing() self.log("开始绘图") def stop_drawing(self): """停止绘图""" self.robot.stop_drawing() self.log("停止绘图") def canvas_click(self, event): """画布点击事件处理""" # 检查是否点击在机械臂机身末尾位置 if self.is_near_base(event.x, event.y): self.dragging_arm = True self.drag_start_x = event.x self.drag_start_y = event.y self.log("开始拖动机器人底座") elif self.current_mode.get() == 0: # 手动绘图模式 # 移动机械臂到点击位置 self.robot.move_to(event.x, event.y) self.draw_workspace() # 开始绘图 self.manual_drawing = True self.robot.start_drawing() self.robot.add_drawing_point(event.x, event.y) # 添加第一个点 self.log(f"开始在位置 ({event.x}, {event.y}) 绘图") def is_near_base(self, x, y): """检查是否在机器人底座附近""" distance = math.sqrt((x - self.robot.base_x)**2 + (y - self.robot.base_y)**2) return distance < 30 def canvas_drag(self, event): """画布拖拽事件处理""" if self.dragging_arm: # 计算移动距离 dx = event.x - self.drag_start_x dy = event.y - self.drag_start_y # 更新底座位置 self.robot.base_x += dx self.robot.base_y += dy # 重新计算所有关节位置 self.robot.update_position() # 更新起始点 self.drag_start_x = event.x self.drag_start_y = event.y # 重绘 self.draw_workspace() elif self.current_mode.get() == 0 and self.manual_drawing: # 手动绘图模式 # 移动机械臂到拖拽位置 self.robot.move_to(event.x, event.y) self.robot.add_drawing_point(event.x, event.y) self.draw_workspace() def canvas_release(self, event): """画布释放事件处理""" if self.dragging_arm: self.dragging_arm = False self.log("结束拖动机器人底座") elif self.current_mode.get() == 0 and self.manual_drawing: # 手动绘图模式 # 停止绘图 self.manual_drawing = False self.robot.stop_drawing() self.log("结束绘图") def start_path(self): """开始径运动""" path_type = self.selected_path.get() size = self.path_size_var.get() if path_type == "自定义" and not self.playback_trajectory: self.log("没有自定义轨迹可执行") return self.log(f"开始{path_type}径运动,尺寸: {size}") self.generate_path(path_type, size) def stop_path(self): """停止径运动""" self.robot.stop_drawing() self.log("径运动已停止") def generate_path(self, path_type, size=150): """生成指定类型的径""" center_x = 400 center_y = 300 points = [] if path_type == "圆形": for i in range(0, 360, 10): angle = math.radians(i) x = center_x + size * math.cos(angle) y = center_y + size * math.sin(angle) points.append((x, y)) elif path_type == "方形": half_size = size // 2 # 上 for x in range(center_x - half_size, center_x + half_size, 10): points.append((x, center_y - half_size)) # 右 for y in range(center_y - half_size, center_y + half_size, 10): points.append((center_x + half_size, y)) # 下 for x in range(center_x + half_size, center_x - half_size, -10): points.append((x, center_y + half_size)) # 左 for y in range(center_y + half_size, center_y - half_size, -10): points.append((center_x - half_size, y)) # 回到起点 points.append((center_x - half_size, center_y - half_size)) elif path_type == "三角形": height = size * math.sqrt(3) / 2 points.append((center_x, center_y - height//2)) # 顶点 points.append((center_x + size//2, center_y + height//2)) # 右下角 points.append((center_x - size//2, center_y + height//2)) # 左下角 points.append((center_x, center_y - height//2)) # 回到顶点 elif path_type == "五角星": for i in range(5): # 外角点 angle1 = math.radians(90 + i * 72) x1 = center_x + size * math.cos(angle1) y1 = center_y + size * math.sin(angle1) points.append((x1, y1)) # 内角点 angle2 = math.radians(90 + (i + 0.5) * 72) x2 = center_x + size * 0.4 * math.cos(angle2) y2 = center_y + size * 0.4 * math.sin(angle2) points.append((x2, y2)) points.append(points[0]) # 闭合五角星 elif path_type == "螺旋线": for i in range(100): angle = math.radians(i * 10) radius = size * (1 - i/150) x = center_x + radius * math.cos(angle) y = center_y + radius * math.sin(angle) points.append((x, y)) elif path_type == "心形": for i in range(0, 360, 5): angle = math.radians(i) # 心形曲线方程 x = center_x + 16 * math.sin(angle)**3 y = center_y - (13 * math.cos(angle) - 5 * math.cos(2*angle) - 2 * math.cos(3*angle) - math.cos(4*angle)) points.append((x, y)) elif path_type == "正弦波": for i in range(0, 360, 10): x = center_x + i y = center_y + size * 0.5 * math.sin(math.radians(i * 2)) points.append((x, y)) elif path_type == "字母A": points.extend([ (center_x - size//2, center_y + size//2), (center_x, center_y - size//2), (center_x + size//2, center_y + size//2), (center_x - size//4, center_y), (center_x + size//4, center_y) ]) elif path_type == "字母B": points.extend([ (center_x - size//2, center_y - size//2), (center_x - size//2, center_y + size//2), (center_x, center_y + size//2), (center_x + size//4, center_y + size//4), (center_x, center_y), (center_x + size//4, center_y - size//4), (center_x, center_y - size//2), (center_x - size//2, center_y - size//2) ]) elif path_type == "数字8": for i in range(0, 360, 10): angle = math.radians(i) x = center_x + size * 0.5 * math.sin(angle) y = center_y + size * 0.3 * math.cos(angle) * (1 + 0.5 * math.sin(angle)) points.append((x, y)) elif path_type == "星形": for i in range(8): outer_angle = math.radians(45 * i) inner_angle = math.radians(45 * i + 22.5) points.append(( center_x + size * math.cos(outer_angle), center_y + size * math.sin(outer_angle) )) points.append(( center_x + size * 0.5 * math.cos(inner_angle), center_y + size * 0.5 * math.sin(inner_angle) )) points.append(points[0]) # 闭合形状 elif path_type == "花朵": for i in range(0, 720, 5): angle = math.radians(i) r = size * (0.5 + 0.5 * math.sin(5 * angle)) x = center_x + r * math.cos(angle) y = center_y + r * math.sin(angle) points.append((x, y)) elif path_type == "迷宫": # 简单的迷宫径 points.extend([ (center_x - size//2, center_y - size//2), (center_x - size//2, center_y + size//2), (center_x - size//4, center_y + size//2), (center_x - size//4, center_y + size//4), (center_x + size//4, center_y + size//4), (center_x + size//4, center_y - size//4), (center_x - size//4, center_y - size//4), (center_x - size//4, center_y - size//2), (center_x + size//2, center_y - size//2), (center_x + size//2, center_y + size//2), (center_x + size//4, center_y + size//2), (center_x + size//4, center_y + size//4) ]) elif path_type == "螺旋方": for i in range(100): t = i / 10 x = center_x + size * 0.5 * (math.cos(t) + math.cos(3*t)/3) y = center_y + size * 0.5 * (math.sin(t) + math.sin(3*t)/3) points.append((x, y)) elif path_type == "自定义": points = self.playback_trajectory # 开始径运动 self.follow_path(points) def follow_path(self, points): """沿着径点运动""" if not points: return # 开始绘图 self.robot.start_drawing() # 移动到第一个点 self.robot.move_to(points[0][0], points[0][1]) self.robot.add_drawing_point(points[0][0], points[0][1]) # 设置动画 self.path_points = points self.path_index = 1 self.follow_next_point() def follow_next_point(self): """移动到下一个径点""" if self.path_index < len(self.path_points): x, y = self.path_points[self.path_index] # 检查是否需要避障 if self.avoid_var.get(): waypoints = self.robot.avoid_obstacles(self.robot.x, self.robot.y, x, y) else: waypoints = [(x, y)] # 移动到每个径点 self.follow_waypoints(waypoints, self.path_index) else: self.robot.stop_drawing() self.log("径运动完成") def follow_waypoints(self, waypoints, path_index): """移动到一系列径点""" if not waypoints: self.path_index += 1 self.follow_next_point() return # 获取下一个径点 x, y = waypoints[0] waypoints = waypoints[1:] # 移动到该点 self.robot.move_to(x, y) self.robot.add_drawing_point(x, y) self.draw_workspace() # 移动到下一个径点 if waypoints: self.after(50, lambda: self.follow_waypoints(waypoints, path_index)) else: self.path_index += 1 self.after(50, self.follow_next_point) def draw_workspace(self): """绘制工作区""" self.canvas.delete("all") # 绘制网格背景 self.draw_grid() # 绘制障碍物 self.draw_obstacles() # 绘制机器人 self.draw_robot() # 绘制绘图径 self.draw_drawing_path() # 更新坐标显示 self.coord_var.set(f"末端坐标: ({self.robot.x:.1f}, {self.robot.y:.1f})") angles = f"J1 {self.robot.joint1_angle:.1f}°, J2 {self.robot.joint2_angle:.1f}°, " angles += f"J3 {self.robot.joint3_angle:.1f}°, J4 {self.robot.joint4_angle:.1f}°" self.angle_var.set(f"关节角度: {angles}") def draw_grid(self): """绘制网格背景""" # 绘制网格 for x in range(0, 800, 50): self.canvas.create_line(x, 0, x, 600, fill="#F0F0F0", dash=(1, 4)) for y in range(0, 600, 50): self.canvas.create_line(0, y, 800, y, fill="#F0F0F0", dash=(1, 4)) # 绘制工作区界 self.canvas.create_rectangle(50, 50, 750, 550, outline="#999", width=2, dash=(2, 2)) self.canvas.create_text(400, 30, text="工业机器人工作区", font=("Arial", 12, "bold")) def draw_obstacles(self): """绘制障碍物""" for obstacle in self.robot.obstacles: x1, y1, x2, y2 = obstacle self.canvas.create_rectangle(x1, y1, x2, y2, fill="#FF9999", outline="#FF0000", width=2) self.canvas.create_text((x1+x2)/2, (y1+y2)/2, text="障碍物", fill="#990000") def draw_robot(self): """绘制工业机器人""" # 绘制底座 self.canvas.create_rectangle( self.robot.base_x - 40, self.robot.base_y - 15, self.robot.base_x + 40, self.robot.base_y + 15, fill="#555", outline="#333", width=2 ) self.canvas.create_oval( self.robot.base_x - 25, self.robot.base_y - 25, self.robot.base_x + 25, self.robot.base_y + 25, fill="#666", outline="#333", width=2 ) # 绘制关节1 self.canvas.create_line( self.robot.base_x, self.robot.base_y, self.robot.joint1_x, self.robot.joint1_y, fill="#4A6FA5", width=14, capstyle=tk.ROUND ) self.canvas.create_oval( self.robot.joint1_x - 16, self.robot.joint1_y - 16, self.robot.joint1_x + 16, self.robot.joint1_y + 16, fill="#F0A202", outline="#333", width=2 ) # 绘制关节2 self.canvas.create_line( self.robot.joint1_x, self.robot.joint1_y, self.robot.joint2_x, self.robot.joint2_y, fill="#4A6FA5", width=12, capstyle=tk.ROUND ) self.canvas.create_oval( self.robot.joint2_x - 14, self.robot.joint2_y - 14, self.robot.joint2_x + 14, self.robot.joint2_y + 14, fill="#F0A202", outline="#333", width=2 ) # 绘制关节3 self.canvas.create_line( self.robot.joint2_x, self.robot.joint2_y, self.robot.joint3_x, self.robot.joint3_y, fill="#4A6FA5", width=10, capstyle=tk.ROUND ) self.canvas.create_oval( self.robot.joint3_x - 12, self.robot.joint3_y - 12, self.robot.joint3_x + 12, self.robot.joint3_y + 12, fill="#F0A202", outline="#333", width=2 ) # 绘制关节4 self.canvas.create_line( self.robot.joint3_x, self.robot.joint3_y, self.robot.joint4_x, self.robot.joint4_y, fill="#4A6FA5", width=8, capstyle=tk.ROUND ) self.canvas.create_oval( self.robot.joint4_x - 10, self.robot.joint4_y - 10, self.robot.joint4_x + 10, self.robot.joint4_y + 10, fill="#F0A202", outline="#333", width=2 ) # 绘制末端执行器 self.canvas.create_line( self.robot.joint4_x, self.robot.joint4_y, self.robot.x, self.robot.y, fill="#333", width=6, capstyle=tk.ROUND ) # 绘制末端执行器(笔尖) self.canvas.create_oval( self.robot.x - 8, self.robot.y - 8, self.robot.x + 8, self.robot.y + 8, fill="#E71D36", outline="#333", width=2 ) # 如果正在绘图,绘制笔头 if self.robot.pen_down: self.canvas.create_line( self.robot.x, self.robot.y, self.robot.x, self.robot.y + 25, fill=self.robot.pen_color, width=self.robot.pen_width ) self.canvas.create_oval( self.robot.x - 6, self.robot.y + 25, self.robot.x + 6, self.robot.y + 37, fill=self.robot.pen_color, outline="#333", width=1 ) def draw_drawing_path(self): """绘制绘图径""" for segment in self.robot.drawing_path: if len(segment["points"]) > 1: # 根据样式设置dash参数 dash = None if segment["style"] == "dash": dash = (10, 5) elif segment["style"] == "dot": dash = (2, 4) elif segment["style"] == "dashdot": dash = (10, 5, 2, 5) # 绘制线段 self.canvas.create_line( segment["points"], fill=segment["color"], width=segment["width"], dash=dash, smooth=True ) def animate(self): """动画循环""" # 更新显示 self.draw_workspace() self.after(50, self.animate) def go_home(self): """归位""" self.robot.set_angles(45, -30, 20, -10) self.draw_workspace() self.log("机器人已归位") def clear_canvas(self): """清除画布""" self.robot.drawing_path = [] self.draw_workspace() self.log("画布已清除") def update_speed(self, value): """更新速度""" self.log(f"速度设置为: {float(value):.1f}") def toggle_record_trajectory(self): """切换轨迹记录状态""" self.recording = not self.recording if self.recording: self.playback_trajectory = [] self.record_btn.config(text="停止记录") self.log("开始记录轨迹") else: self.record_btn.config(text="开始记录轨迹") self.log(f"停止记录轨迹,共记录 {len(self.playback_trajectory)} 个点") def playback_trajectory(self): """回放轨迹""" if not self.playback_trajectory: self.log("没有可回放轨迹") return # 设置径为自定义 self.selected_path.set("自定义") self.start_path() def save_trajectory(self): """保存轨迹到文件""" if not self.playback_trajectory: self.log("没有轨迹可保存") return try: file_path = filedialog.asksaveasfilename( defaultextension=".csv", filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")] ) if file_path: with open(file_path, "w", newline="") as f: writer = csv.writer(f) writer.writerow(["X", "Y"]) for x, y in self.playback_trajectory: writer.writerow([x, y]) self.log(f"轨迹已保存到 {file_path}") except Exception as e: self.log(f"保存轨迹失败: {str(e)}") def load_trajectory(self): """从文件加载轨迹""" try: file_path = filedialog.askopenfilename( filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")] ) if file_path: self.playback_trajectory = [] with open(file_path, "r") as f: reader = csv.reader(f) next(reader) # 跳过标题行 for row in reader: if len(row) >= 2: self.playback_trajectory.append((float(row[0]), float(row[1]))) self.log(f"从文件加载了 {len(self.playback_trajectory)} 个轨迹点") except Exception as e: self.log(f"加载轨迹失败: {str(e)}") def clear_trajectory(self): """清除轨迹""" self.playback_trajectory = [] self.log("轨迹已清除") def save_drawing(self): """保存绘图为图片""" try: file_path = filedialog.asksaveasfilename( defaultextension=".png", filetypes=[("PNG图片", "*.png"), ("JPEG图片", "*.jpg"), ("所有文件", "*.*")] ) if file_path: # 创建画布的PostScript表示 self.canvas.postscript(file=file_path + ".eps", colormode="color") # 转换为PNG img = Image.open(file_path + ".eps") img.save(file_path, "png") os.remove(file_path + ".eps") self.log(f"绘图已保存为 {file_path}") except Exception as e: self.log(f"保存绘图失败: {str(e)}") def load_drawing(self): """加载绘图""" try: file_path = filedialog.askopenfilename( filetypes=[("图片文件", "*.png;*.jpg;*.jpeg"), ("所有文件", "*.*")] ) if file_path: # 清除当前绘图 self.robot.drawing_path = [] # 加载图片 img = Image.open(file_path) img = img.resize((800, 600)) self.photo_img = ImageTk.PhotoImage(img) # 在画布上显示图片 self.canvas.create_image(0, 0, image=self.photo_img, anchor="nw") self.log(f"已加载绘图: {os.path.basename(file_path)}") except Exception as e: self.log(f"加载绘图失败: {str(e)}") def log(self, message): """记录日志""" self.log_text.config(state="normal") timestamp = time.strftime("%H:%M:%S") self.log_text.insert(tk.END, f"[{timestamp}] {message}\n") self.log_text.see(tk.END) self.log_text.config(state="disabled") def add_obstacle(self): """添加障碍物""" # 随机位置添加障碍物 x = random.randint(100, 700) y = random.randint(100, 500) size = random.randint(50, 100) self.robot.add_obstacle(x, y, x+size, y+size) self.draw_workspace() self.log(f"添加障碍物: ({x}, {y}) - ({x+size}, {y+size})") def clear_obstacles(self): """清除所有障碍物""" self.robot.clear_obstacles() self.draw_workspace() self.log("所有障碍物已清除") if __name__ == "__main__": root = tk.Tk() root.title("工业机器人仿真系统 - 绘图功能") root.geometry("1200x800") # 设置主题 style = ttk.Style() style.theme_use("clam") style.configure("TButton", padding=6) style.configure("TFrame", background="#F5F5F5") # 设置字体 default_font = ("Microsoft YaHei", 9) root.option_add("*Font", default_font) app = SimulationFrame(root) app.pack(fill="both", expand=True) root.mainloop()开始径无法画出正常的径图也不进行避障,要 能手动设置障碍物,机械臂只要一个可以活动的关节,纯python,给出完整代码
06-06
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值