From 94c6589f09a19a6c4059aaa17203513069f29151 Mon Sep 17 00:00:00 2001 From: danielb123456 Date: Mon, 23 Feb 2026 03:20:36 -0500 Subject: [PATCH 1/2] Firmware and Software Onboardings Complete --- .gitignore | 5 + graph_pwm.py | 27 ++++ pwm_config.png | Bin 0 -> 7190 bytes .../.devcontainer/devcontainer.json | 27 ++++ .../uwrt_software_trainining/.gitignore | 1 + .../uwrt_software_trainining/Dockerfile.dev | 15 ++ .../uwrt_software_trainining/README.md | 29 ++++ .../software_training/CMakeLists.txt | 93 ++++++++++++ .../software_training/action/Software.action | 16 ++ .../cmd_vel_moving_turt_publisher.hpp | 43 ++++++ .../moving_turtle_action_server.hpp | 58 +++++++ .../reset_moving_turtle_service.hpp | 44 ++++++ .../spawn_turtle_nodelet.hpp | 44 ++++++ .../software_training/turtle_neutralize.hpp | 32 ++++ .../software_training/turtle_publisher.hpp | 37 +++++ .../include/software_training/visibility.h | 65 ++++++++ .../launch/software_training.launch.py | 47 ++++++ .../software_training/msg/Software.msg | 3 + .../software_training/package.xml | 40 +++++ .../src/cmd_vel_moving_turt_publisher.cpp | 35 +++++ .../src/moving_turtle_action_server.cpp | 141 ++++++++++++++++++ .../src/reset_moving_turtle_service.cpp | 67 +++++++++ .../src/spawn_turtle_nodelet.cpp | 64 ++++++++ .../src/turtle_neutralize.cpp | 47 ++++++ .../src/turtle_publisher.cpp | 67 +++++++++ .../software_training/srv/Software.srv | 3 + three_turtles.png | Bin 0 -> 11191 bytes turtle_move.png | Bin 0 -> 11879 bytes 28 files changed, 1050 insertions(+) create mode 100644 .gitignore create mode 100644 graph_pwm.py create mode 100644 pwm_config.png create mode 100644 software_training_assignment/uwrt_software_trainining/.devcontainer/devcontainer.json create mode 100644 software_training_assignment/uwrt_software_trainining/.gitignore create mode 100644 software_training_assignment/uwrt_software_trainining/Dockerfile.dev create mode 100644 software_training_assignment/uwrt_software_trainining/README.md create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/CMakeLists.txt create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/action/Software.action create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/include/software_training/cmd_vel_moving_turt_publisher.hpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/include/software_training/moving_turtle_action_server.hpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/include/software_training/reset_moving_turtle_service.hpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/include/software_training/spawn_turtle_nodelet.hpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/include/software_training/turtle_neutralize.hpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/include/software_training/turtle_publisher.hpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/include/software_training/visibility.h create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/launch/software_training.launch.py create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/msg/Software.msg create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/package.xml create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/src/cmd_vel_moving_turt_publisher.cpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/src/moving_turtle_action_server.cpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/src/reset_moving_turtle_service.cpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/src/spawn_turtle_nodelet.cpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/src/turtle_neutralize.cpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/src/turtle_publisher.cpp create mode 100644 software_training_assignment/uwrt_software_trainining/software_training/srv/Software.srv create mode 100644 three_turtles.png create mode 100644 turtle_move.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb069af --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/ +install/ +log/ +venv/ +__pycache__/ diff --git a/graph_pwm.py b/graph_pwm.py new file mode 100644 index 0000000..7fcd90e --- /dev/null +++ b/graph_pwm.py @@ -0,0 +1,27 @@ +import matplotlib.pyplot as plt +import numpy as np +from scipy import signal + +clock_speed = 84000000 +prescaler = 839 +period = 1999 +pulse = 1000 + +# okay so pwm_freq = clock_speed / prescaler * period +# duty cycle = pulse / period + +timer_tick_freq = clock_speed / (prescaler + 1) +pwm_freq = timer_tick_freq / (period + 1) +duty_cycle = (pulse / (period + 1)) * 100 + +time = np.linspace(0, 0.06, 1000) +wave = signal.square(2 * np.pi * pwm_freq * time, duty=duty_cycle/100) + +plt.figure(figsize=(10, 4)) +plt.plot(time * 1000, wave, color='green', linewidth=2) +plt.title(f'PWM Output ({pwm_freq}Hz, {duty_cycle}% Duty Cycle)') +plt.xlabel('Time (ms)') +plt.ylabel('On/Off') +plt.ylim(-1.5, 1.5) +plt.grid(True) +plt.show() \ No newline at end of file diff --git a/pwm_config.png b/pwm_config.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ce212b115c2fdbf473be257a35b11e33c83e6d GIT binary patch literal 7190 zcmZWubzGC(`yMbF0Rb63P)0XMcMC`>AWEmSbc_*_(j`+G2}K$KX{4k_dIW0p zntYb$zm3__*sMm&I6vptPa9E?FJM6^S^d0AymLR4R0PKlFM%h|v}5Q=m}h{@5V*}> zcz@XaJFVW)J`RB>ayW4REKqnYv0W^2pSXDGbbD$g--`q`BKzkAxl`mP&D)h*OD20E zi{4CvU0I`?mPLBMA+ImSCDDG&Io&kY?UrCqWGezKYnuFpc8MD`p))lzP`DV4o8N|0 zi7#izjmW&b7!Syk`Hzk1JhIrqbV;D9vc=f$pXB2Gn5_V(1 z`0eZ>Xw!Uob?y$&YQvv36zTOhC;avv_6AtUV*A@}S0v)*$b4`onGxT8bN^uf=8ssW zulc~P;e+zCW|tOpnH>0Z8Fdryb`1n8x@bz@-r3%*(IM-hwKRLwAbv6Kdx)+~tRP&P zJ>GGV_CMneLPp7j_uNxxj2!=daa_S`zX%Qv-M>759hTqD;pPA=cfnV0j?0CIMtAXx zzi)KnfDB}?%9i%8+^${L2JDOjb_OO3`nHhs?6>(l1y6f*yXAt9x=!?nUUR$C`+9^s(U;djD-#|()Z&e#p^rx--=znE7&Xo@y3)qf!u#vDCk zix=iX(sO4=ICA%sF>{xy6Ex&;ZwF1dEnxVRdPS}hSvCB$!E^3U=jj82u*?@n#P#!$ zN#}lWJ;Gt8v5E*`xT|&Ay+e5Z{IlLyWxs8-2YKrd$621S)6>4PlUR9;##jO2V+qQu zg35&g@@1z*xu<*$+NwP$O&(9$Y9qUbHs$<-qU~IxYY5U@AeHHyX2QIb?`#hj{({cwMn7Gvu=byKjNP3IV3-b;wK z7geq2_3}vr+??-+6KPei)k+ZAp>R33f!yb>y_8d5FgZ z?`pNqSReUAzEzhuNabbipHk*NsxSlEdV2mjpF+Pys`Q3>3+ z%WBg>j!D1-R67%*jn|A_FX|QcydY0XxzLBCi_W&55=K~^Fg`(TaSnG8NX>Xf1m|7C z^+#S%fw(vBsHXP#o85-gXLzWEX}1+_vO$LinA zzGajDpvlzAj(hd~z%1cA)4N!$lK8E@(d8aj0}{3I4>nJabFMG|8IlwxcMjcpjN_2; z8drZrC#)OSuo~-!t7YqqXAY%ijR@LbK76DHy*UbSfpLlU>%g*dQ3n&9_k zIRw1PM%b733{Fq#jPXdE?qz`TJiXVRcl2J*30xqk>fDeF_2sxHS3Smo96asPMd>XI zIzz%sRpJ>GJsX}4jduiZ6(&o!4y8=1#U&f-!)WWCnnt*;3 zmbc&I?#=8WxiiE~O3BwiGVIdjXe!KOM@u(B@}^Q;(rCdCSecn%p9Nj0W+%DeMi!mN zv3QLL3fHmU4gQ``M(!+-E4W0w{?&d^dxR17(i zS;%kzQyJq;0PvRi>vP_GZ1z^XhX|yy7$aF^J2dm&lNvs=^wNGsPY~uO+|OAnvq%v* z)A8u{&jd?jg;OTzaZM`p>*(W#vgq{bR81@{8*~T_r#zuxz5IH3>|_|>NK*)%mO!sX zL8PLEWMiyLW$Q4O#zO7PmMBl*{{DDNLV(}R%M|njg8NvvP?A7XxFCe*ScUwf-%bxc zArk1#F#M2?UFSQ#8Q*sBq~Y;Kt6iO>zV{u;iv#7-SbB7@?xH#FMX7}c1sF5K2N)0q zCa|LJC#&!HYfWT)pa^8)l#g&Wf5zGIp*~J&Yls>ElDCvo1JeyamZ?f}kh2cuVpywo z2RZk@Vo*pSfYXU;QQyG@DIQ*_XzYAdo=c%l@6OR6(q)+|Ny9cguc(38$I|;*0ESZn z<pdh>3-908E^natD_tDN>N{jaHR|UBi zm}ng0lK{?%t6S-)>H?S}LOP81W_?Qg_6^b)C&0mH3Lk54U}J zqbwzUquAyt+IwDc#8SgwX>9if`)A|pAoXAik zs_KB!3!bv;i72x?FBigJ%Ny2mbnG;U4rs$7=K4_&D3i8(>AccrTOgtZcN-5hX(-<7 zuF$8jo7?rUQf@>p3W5;IUR~#1T8PrtH9#}1cKzVZbTwb+B^G-f!qoubeUPhEPu`t| z!Ej2q*%8T4rh;6(gl-Pt^%7gyM~WSb3P5r z!#RTTK8a~QcUjtcBg3#v5a>s?*mggMymOq13H-e4D)F1;+Qzad(~D-Zc86<8x+&7j zyyA5(IwSCEZJbcUd3u?#q+jUU1+19;)jtBTD@w$(fY-Y%1&fzN6?}|K5&PX~4f>D; zzgI-{E|WgLo@2TGrP^z@eQJ_BV~^b`P2K2abSCFGL7NZpjjcx_kFslU2gmN88P(^W zNkrs5qA8FtcCXR$ByC;K2OEBx(_ZJC-E_X(XP6pZqy5y1CX(5`Ur@gnaYZp$WK`<; z4bUI)yAY~7C!t*E757-Kiw{Q;wo7`@!7Fyk;)4kN$TMR3B`9wG7Yp7S1PH%~Oi4r| zs?^U1_?m3RC5E*;q!XA%z0f(34w?@9Luo|`oWWvKqiXFa z9l`)C2+tbiHnO~Wb?JcKz!UpPmzlzNcv2fE-iXS{bsPu+k4E|P5V(b~)_+*vv~TpS z0(MG=WztFDnx;&@r$+5@pPWSKb@-g|Y1cn=BLeGd{5HX1cjBusQ!XL=?D1B9Q^_~F zzCPr=-dmnf-efIS3jtZo{{sN)MionkGE1#C)uN{Bm4%_Z)g_N`3>tFH`y9f~0cS6@ zmzpi-!#0b{OfL8^;gRUxMZ6%!gAi)v3L}IH>yg?| zci*p-Ids=R*w?S~@-QO0qO7PA51`WE1a}9lqKJN_PYiqT@*C$Y<{C@9FbvEioImn) zj765JYz$Ky@hdoi@p4KQT+El&RuW!texp_QMwl za!B;yeh_^_2Gg=?cg^%|*adXk7ORm}UpWvO8Lfv78dZ56&|DYloXxm#lm#K@8f|5I zt(Om!hs^uf8#3H66P^CqsR0J&EPPxxOCwuN@_KHQ6m(xu#g6$FHkriJ)BH+q$0@$9 ztt+-W??KT1X2s2WTN!WXhdXICkeAxoZ@b>G&yI-b`?E)T4J!RsI;`Q2LQV@z^LWaB zsD^O264zNOPKKO(fSnu8z@-s13ZcR4U(yolEba;Py40`KK=vJ$}ANWwj^h zxGHeGc~&l!xE)VHngw4_2Iiz{1iK2uT0@=uUhhWf{A?==0)&JImVSlKSGjglJ!N38 zhRCmYB+DPgbW`QGCKD=R@5W!+3C+^O*C=B)1u5YeUD3^>fcd%ZR%@_o$<;z*rPAsB zSeft;xN3Fk_R=-9I{H^EH6i~>kjD=&l8RipSGVdPjndD8*^LD)!H?Oj{%YhvLD7Fw zF~8Py#^F}3cgv!&8UmD+r=oC4@wPsWnc%)PX8(E*R5f-wXZoh+Qrvl}Lura#k#(B| zddB!n$^^R8ygXBYi8;LE|iFON-C$$`l@=eHQ3^s%E0Ut!pyV;9X!f*U+q=-&ZO zx^O}Ei?#2~WgQs+oI;o?mkeKoFKmmON)_`SN6(6;G80&7tWh~Xvf}4?A%-`vBoerA z-FGPDDbz0o&)$UWQ`vkuhXwY5eVe_-N-t9XnIk6quvUx7W~DvfrHO3tb;pK(<>;5X z_YtyhXj6R-^QO-jKRatNQ-jvIy_yx;%Zj;Gz)494Rl~WpJk`h!ncCmA%XLDY{;7I) z+e4A!p@Fb_8--yiQ)hLynC&wW#_7ypMfKJ|%7?PTCR2R5YQ8(;O#j2b-4C_(RA0Ts z=)LbGK+LM?6EOo3Nf~G^*eXn!{Gs-rSCb>;qSByo7IIDfQ)kP^TB1s>967e|9f;M4 z1vfOqFlpppdKF=jco@c96ZtQ_4mcBHc=o+aH|~=t46J-XMc%cSXa_wlmE| z{Fh1F7}Xk;j@Zb3E-28kt_FXwhv#UMgw}#QpQZlcJ15x?)irkQ%=qPb*O&X4p00AC z^j%|TOt);ZR#4O;tV4BWr<1BB`0AN-8g}aD7@U&{Rh^k1x_Ly|+ievox-}{!f;ViD z;hvFYq}3-J(YpF-h-9}v^IF0?ubqNe{6`(P(s;&soPyLz->6WVMdsF@EPKPV;Km^# zkzz-TV6?B}RkClA4dCtbCu2FnZrW9?w-jPT1da-2;>Ampe41Tz0nG}WcA$raL?Xv% zl+k=B+b!}P(kbFuo(Bav4q}T5AB7$0CW7QBf>mu-n)|&RhN>uL<%f9AZ81Ru8qh78 zR`LqSI_MNH{cr>{wKe%b!xuZTvhv|L6A<&M22}sDWw|k+o|gB zt!*)bNijX}&bP?;7oV5yOr%+`13}qL?5Je&W^a(oJcCQV38JHTLCJnf8)4U=r?go0 zVa}aejHzR6wvpa*0%Wv3CT`1?_a?hu(Z)tGtqljTHT~I_ZI;?wvW=USU8Z;bC|&&C zKari#o%0V9YFH^SXnd4r*Zrxx4kQ`K_t+Fg>25s_gWSvXMFpUe&~1SEMFUy@#uScP zdah#UmEr^Dd9{BemRl^A<2-4lS$s036Dux5d)VVPYe-#Y%u7}^H~Y;(@Cf^Lm9Nx> zcNk)#R$kI(vsGp2$xY

GT!90`cjdBF$A_Jho^0M2K@F=UK| zo0DD!-?hiQ?V4ALVNcqQ4X3yqTzByjAnbCBF*El6iy`A_ZPN7p8>4G{!qziB4JQ{Q zIXgGS^N;TgLk62L25t48PqUJjyW^!}^-J2JXCc3sdbQ6#L3JLDz+GN`60>x0nX~~t z4m-vgwRQC&_H8A*PNj)`j>@RbLrsBKMr9 zAR)?FY$JR+(vE?6SBCJj7HI?f^tUhfPIR8|KNtN(&qti52{n4r&O=x!efqQ$Z(k=H zUURRqnCaO^y7=imhlmQ_M3pwMmP}1T({SloU2}lw7}INW?C4#Vx@Te+S9zuEC{Srx zDd^MSIp1e4)X1Awk^)}%IbYT)biId8J&TQ18B;y;C}#2hUfV&U^_K`8nK2z4j4#jh zFYo~$U8n3NscMbvyuf#Zh~m!9L=`9hC*}TY>Hkx#OI50Z#IzpAy{cSs#2A?x8#1J$ z2U8G+tm-k0+RKuh`R=tR#^U$S#Wy$xzq?jmq>P3%s-JO<0LMo>uHx9t!NsGFk1kYP zi^-EBm6oo`>kWbyk9?wKfr}FgwDKczzRn-s%c!G0lh4^VHRCtBB#w1VbWU_QK)x?2 z@5I*#7d&oz_wu>nDjafaUEF;SAGJ9p#{aqiyx((`%fJ$uO5I79jB@ey3}*BcfY6_6 za+>wFc^zMSdHE(-W-eqznRsGFHpjFJ+bQ(q%yc%3>RI8qu`R?JVv1j5mQ81EwTfS2 zcXi>lwePN4$R3ARdRgz2>W&xU$`owuomgh#jmFo z(@J^0O6310u-@cp9DrBU#uQSw8~5|CtfC@u{8>BjL8eSNHn`&%@cW@HLv#AeNJZC>7pJ2xJ z9jmM?h%N|(_1-Cd$8k4=qZ$C%aD6yVxiC@4zuj*LvqEd`m@CrCB=1qbYu%8^8jXNG z+U|M8%C+lUxE}wgoNTpI)vD-&N1rF%U*Y5?tv%Kxb&~EKTlSVMM%c}El+825^?3T??W$Sdt?2Y;bjNre@4|%p20wK|`c;+s59_1=xa?TFKe*=n; zBfBnA;!CEnXP4tOS1U>Lf@%->*|C~r{zq@&2S$_1-Fq&n%+K7NgS#he>V)-dpL}~k z9;qMcFqv>=-yI_?$NXnV^dqA%GW@l{cV{gA@9b|+!dtf&ax3DC z*aBKJIooIoYNjoG)@3K^NE}+(ex(L~($GbTWx)DvPp~^f-*-c8}vGw5gWoaS+=5i;+L z)cRCGCGBd4eZv#!(;yDk0Og;X^AucacA6ZrVC)Mo$`xN`o@9Chky|h9*#l#qZQfu_ zf@|{l8zDMyl#anFMJhjYf5?Sa+nl7hYhP-bCcaf?=+j2ZmRZD2^Rd4=J*FbJ4e?XA z+81F(0l!L-9mCxcZG)E23TdMh<}wT9ih$XFX49vwtpZzy%;NTcT+;}jj-MBhwZNMC z6I;ovlm~1n8y;Ifw<8ddWABlo?TYA?Las2!= ztGW8BIuYIWy(3Yufq\n") + +add_library(turtle_spawn SHARED src/spawn_turtle_nodelet.cpp) +target_compile_definitions(turtle_spawn PRIVATE "COMPOSITION_BUILDING_DLL") +ament_target_dependencies(turtle_spawn "rclcpp" "rclcpp_components" "turtlesim" "std_msgs") +rclcpp_components_register_nodes(turtle_spawn "composition::spawn_turtle_nodelet") +set(node_plugins "${node_plugins}composition::spawn_turtle_nodelet;\\$\n") + +add_library(turtle_pub SHARED src/turtle_publisher.cpp) +target_compile_definitions(turtle_pub PRIVATE "COMPOSITION_BUILDING_DLL") +ament_target_dependencies(turtle_pub "rclcpp" "rclcpp_components" "turtlesim" "std_msgs") +rosidl_target_interfaces(turtle_pub ${PROJECT_NAME} "rosidl_typesupport_cpp") +rclcpp_components_register_nodes(turtle_pub "composition::turtle_publisher") +set(node_plugins "${node_plugins}composition::turtle_publisher;\\$\n") + +add_library(turtle_service SHARED src/reset_moving_turtle_service.cpp) +target_compile_definitions(turtle_service PRIVATE "COMPOSITION_BUILDING_DLL") +ament_target_dependencies(turtle_service "rclcpp" "rclcpp_components" "turtlesim" "std_msgs") +rosidl_target_interfaces(turtle_service ${PROJECT_NAME} "rosidl_typesupport_cpp") +rclcpp_components_register_nodes(turtle_service "composition::reset_moving_turtle_service") +set(node_plugins "${node_plugins}composition::reset_moving_turtle_service;\\$\n") + +add_library(cmd_vel_publisher SHARED src/cmd_vel_moving_turt_publisher.cpp) +target_compile_definitions(cmd_vel_publisher PRIVATE "COMPOSITION_BUILDING_DLL") +ament_target_dependencies(cmd_vel_publisher "rclcpp" "rclcpp_components" "turtlesim" "geometry_msgs" "std_msgs") +rclcpp_components_register_nodes(cmd_vel_publisher "composition::cmd_vel_moving_turt_publisher") +set(node_plugins "${node_plugins}composition::cmd_vel_moving_turt_publisher;\\$\n") + +add_library(turtle_action_server SHARED src/moving_turtle_action_server.cpp) +target_compile_definitions(turtle_action_server PRIVATE "SOFTWARE_TRAINING_DLL") +ament_target_dependencies(turtle_action_server "rclcpp" "rclcpp_components" "turtlesim" "rclcpp_action" "std_msgs" "geometry_msgs") +rosidl_target_interfaces(turtle_action_server ${PROJECT_NAME} "rosidl_typesupport_cpp") +rclcpp_components_register_node(turtle_action_server PLUGIN "composition::moving_turtle_action_server" EXECUTABLE moving_action_server) + +install(TARGETS + turtle_request + turtle_spawn + turtle_pub + turtle_service + turtle_action_server + cmd_vel_publisher + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + +install(DIRECTORY launch DESTINATION share/${PROJECT_NAME}) + +ament_package() \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/action/Software.action b/software_training_assignment/uwrt_software_trainining/software_training/action/Software.action new file mode 100644 index 0000000..ac77e1c --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/action/Software.action @@ -0,0 +1,16 @@ +# Goal Request /moving_turtle/cmd_vel +geometry_msgs/Vector3 linear_pos +geometry_msgs/Vector3 angular_pos + +# Result - time +--- +uint64 duration + +--- +# Feedback /moving_turtle/pose +float32 x_pos +float32 y_pos +float32 theta_pos + + + diff --git a/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/cmd_vel_moving_turt_publisher.hpp b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/cmd_vel_moving_turt_publisher.hpp new file mode 100644 index 0000000..0130798 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/cmd_vel_moving_turt_publisher.hpp @@ -0,0 +1,43 @@ +#ifndef CMD_VEL_MOVING_TURT_PUBLISHER_HPP_ +#define CMD_VEL_MOVING_TURT_PUBLISHER_HPP_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace composition { + +class cmd_vel_moving_turt_publisher : public rclcpp::Node { + +public: + SOFTWARE_TRAINING_PUBLIC + explicit cmd_vel_moving_turt_publisher(const rclcpp::NodeOptions &options); + +private: + rclcpp::Publisher::SharedPtr publisher; + rclcpp::TimerBase::SharedPtr timer; + + static constexpr unsigned int QUEUE{10}; + + struct coordinates { + struct linear { + static constexpr float x = 2.0; + static constexpr float y = 0.0; + static constexpr float z = 0.0; + }; + struct angular { + static constexpr float x = 0.0; + static constexpr float y = 0.0; + static constexpr float z = 2.0; + }; + }; +}; + +} // namespace composition + +#endif // CMD_VEL_MOVING_TURT_PUBLISHER_HPP_ diff --git a/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/moving_turtle_action_server.hpp b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/moving_turtle_action_server.hpp new file mode 100644 index 0000000..3e27f27 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/moving_turtle_action_server.hpp @@ -0,0 +1,58 @@ +#ifndef MOVING_TURTLE_ACTION_SERVER_HPP_ +#define MOVING_TURTLE_ACTION_SERVER_HPP_ + +#include +#include +#include +#include + +#include +#include // ros2 time header +#include // ros2 action header +#include +#include + +#include // cmd_vel publisher message +#include // header for message to get moving turt position + +namespace composition{ + +class moving_turtle_action_server : public rclcpp::Node { +public: + SOFTWARE_TRAINING_PUBLIC + explicit moving_turtle_action_server(const rclcpp::NodeOptions &options); + + using GoalHandleActionServer = + rclcpp_action::ServerGoalHandle; + +private: + rclcpp_action::Server::SharedPtr action_server; + rclcpp::Publisher::SharedPtr publisher; + rclcpp::Subscription::SharedPtr subscriber; + + SOFTWARE_TRAINING_LOCAL + rclcpp_action::GoalResponse handle_goal( + const rclcpp_action::GoalUUID &uuid, + std::shared_ptr goal); + + SOFTWARE_TRAINING_LOCAL + rclcpp_action::CancelResponse + handle_cancel(const std::shared_ptr goal_handle); + + SOFTWARE_TRAINING_LOCAL + void handle_accepted(const std::shared_ptr goal_handle); + + void execute(const std::shared_ptr goal_handle); + + float x{0.0f}; + float y{0.0f}; + float theta{0.0f}; + float linear_velocity{0.0f}; + float angular_velocity{0.0f}; + + static constexpr unsigned int QUEUE{10}; +}; + +} // namespace composition + +#endif // MOVING_TURTLE_ACTION_SERVER_HPP_ diff --git a/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/reset_moving_turtle_service.hpp b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/reset_moving_turtle_service.hpp new file mode 100644 index 0000000..645bc56 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/reset_moving_turtle_service.hpp @@ -0,0 +1,44 @@ +#ifndef RESET_MOVING_TURTLE_SERVICE_HPP_ +#define RESET_MOVING_TURTLE_SERVICE_HPP_ + +#include +#include +#include + +#include + +#include +#include +#include + +namespace composition { + +class reset_moving_turtle_service : public rclcpp::Node { + +public: + SOFTWARE_TRAINING_PUBLIC + explicit reset_moving_turtle_service(const rclcpp::NodeOptions &options); + +private: + // create service that will reset turtle to starting point + rclcpp::Service::SharedPtr service; + + // create client + rclcpp::Client::SharedPtr client; + + // server callback + SOFTWARE_TRAINING_LOCAL + void service_callback( + const std::shared_ptr request, + std::shared_ptr response); + + struct reset_coordinates { + static constexpr float x = 25.0; + static constexpr float y = 10.0; + static constexpr float theta = 0.0; + }; +}; + +} // namespace composition + +#endif // RESET_MOVING_TURTLE_SERVICE_HPP_ diff --git a/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/spawn_turtle_nodelet.hpp b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/spawn_turtle_nodelet.hpp new file mode 100644 index 0000000..3934be7 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/spawn_turtle_nodelet.hpp @@ -0,0 +1,44 @@ +#ifndef SPAWN_TURTLE_NODELET_HPP_ +#define SPAWN_TURTLE_NODELET_HPP_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace composition { + +class spawn_turtle_nodelet : public rclcpp::Node { +public: + SOFTWARE_TRAINING_PUBLIC + explicit spawn_turtle_nodelet(const rclcpp::NodeOptions &options); + +private: + rclcpp::Client::SharedPtr client; + rclcpp::TimerBase::SharedPtr timer; + + SOFTWARE_TRAINING_LOCAL + void spawn_turtle(); + + static const unsigned int NUMBER_OF_TURTLES{2}; + + struct turtle_info { + float x_pos; + float y_pos; + float rad; + }; + + std::vector turtle_names{"stationary_turtle", "moving_turtle"}; + std::vector turtle_bio{{5.0, 5.0, 0.0}, {25.0, 10.0, 0.0}}; + + std::map turtle_description; +}; + +} // namespace composition + +#endif // SPAWN_TURTLE_NODELET_HPP_ \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/turtle_neutralize.hpp b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/turtle_neutralize.hpp new file mode 100644 index 0000000..90061d1 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/turtle_neutralize.hpp @@ -0,0 +1,32 @@ +#ifndef TURTLE_SERVICE_REQUEST_NODE_HPP_ +#define TURTLE_SERVICE_REQUEST_NODE_HPP_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace composition { + +class turtle_service_request_node : public rclcpp::Node { +public: + SOFTWARE_TRAINING_PUBLIC + explicit turtle_service_request_node(const rclcpp::NodeOptions &options); + +private: + rclcpp::Client::SharedPtr client; + rclcpp::TimerBase::SharedPtr timer; + + // all the turtles (not turtle1, the moving turtle) + std::vector turtle_names = {"moving_turtle", "stationary_turtle"}; + + SOFTWARE_TRAINING_LOCAL + void kill(); +}; + +} // namespace composition +#endif // TURTLE_SERVICE_REQUEST_NODE_HPP_ \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/turtle_publisher.hpp b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/turtle_publisher.hpp new file mode 100644 index 0000000..6119afc --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/turtle_publisher.hpp @@ -0,0 +1,37 @@ +#ifndef TURTLE_PUBLISHER_HPP_ +#define TURTLE_PUBLISHER_HPP_ + +#include + +#include + +#include +#include +#include + +namespace composition { + +class turtle_publisher : public rclcpp::Node { +public: + SOFTWARE_TRAINING_PUBLIC + explicit turtle_publisher(const rclcpp::NodeOptions &options); + +private: + rclcpp::Subscription::SharedPtr stationary_turt_sub; + rclcpp::Subscription::SharedPtr moving_turt_sub; + rclcpp::Publisher::SharedPtr publisher; + rclcpp::TimerBase::SharedPtr timer; + rclcpp::CallbackGroup::SharedPtr callbacks; + + float x_stationary_turt{0.0f}; + float y_stationary_turt{0.0f}; + float x_moving_turt{0.0f}; + float y_moving_turt{0.0f}; + + float total_distance{0.0f}; + + static const unsigned int QUEUE{10}; +}; +} // namespace composition + +#endif // TURTLE_PUBLISHER_HPP_ \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/visibility.h b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/visibility.h new file mode 100644 index 0000000..7465546 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/include/software_training/visibility.h @@ -0,0 +1,65 @@ +// Copyright 2016 Open Source Robotics Foundation, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOFTWARE_TRAINING__VISIBILITY_H_ +#define SOFTWARE_TRAINING__VISIBILITY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// This logic was borrowed (then namespaced) from the examples on the gcc wiki: +// https://gcc.gnu.org/wiki/Visibility + +#if defined _WIN32 || defined __CYGWIN__ + +#ifdef __GNUC__ +#define SOFTWARE_TRAINING_EXPORT __attribute__((dllexport)) +#define SOFTWARE_TRAINING_IMPORT __attribute__((dllimport)) +#else +#define SOFTWARE_TRAINING_EXPORT __declspec(dllexport) +#define SOFTWARE_TRAINING_IMPORT __declspec(dllimport) +#endif + +#ifdef SOFTWARE_TRAINING_DLL +#define SOFTWARE_TRAINING_PUBLIC SOFTWARE_TRAINING_EXPORT +#else +#define SOFTWARE_TRAINING_PUBLIC SOFTWARE_TRAINING_IMPORT +#endif + +#define SOFTWARE_TRAINING_PUBLIC_TYPE SOFTWARE_TRAINING_PUBLIC + +#define SOFTWARE_TRAINING_LOCAL + +#else + +#define SOFTWARE_TRAINING_EXPORT __attribute__((visibility("default"))) +#define SOFTWARE_TRAINING_IMPORT + +#if __GNUC__ >= 4 +#define SOFTWARE_TRAINING_PUBLIC __attribute__((visibility("default"))) +#define SOFTWARE_TRAINING_LOCAL __attribute__((visibility("hidden"))) +#else +#define SOFTWARE_TRAINING_PUBLIC +#define SOFTWARE_TRAINING_LOCAL +#endif + +#define SOFTWARE_TRAINING_PUBLIC_TYPE +#endif + +#ifdef __cplusplus +} +#endif + +#endif // SOFTWARE_TRAINING__VISIBILITY_H_ diff --git a/software_training_assignment/uwrt_software_trainining/software_training/launch/software_training.launch.py b/software_training_assignment/uwrt_software_trainining/software_training/launch/software_training.launch.py new file mode 100644 index 0000000..cd79bf7 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/launch/software_training.launch.py @@ -0,0 +1,47 @@ +import launch +from launch_ros.actions import Node, ComposableNodeContainer +from launch_ros.descriptions import ComposableNode + +def generate_launch_description(): + """Generate launch description with multiple components.""" + turtlesim_node = Node( + package='turtlesim', + executable='turtlesim_node', + name='sim' + ) + + container = ComposableNodeContainer( + name='software_training_container', + namespace='', + package='rclcpp_components', + executable='component_container', + composable_node_descriptions=[ + ComposableNode( + package='software_training', + plugin='composition::turtle_service_request_node', + name='turtle_request'), + ComposableNode( + package='software_training', + plugin='composition::spawn_turtle_nodelet', + name='turtle_spawn'), + ComposableNode( + package='software_training', + plugin='composition::cmd_vel_moving_turt_publisher', + name='cmd_vel_publisher'), + ComposableNode( + package='software_training', + plugin='composition::reset_moving_turtle_service', + name='turtle_service'), + ComposableNode( + package='software_training', + plugin='composition::turtle_publisher', + name='turtle_pub'), + ComposableNode( + package='software_training', + plugin='composition::moving_turtle_action_server', + name='turtle_action_server'), + ], + output='screen', + ) + + return launch.LaunchDescription([turtlesim_node, container]) \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/msg/Software.msg b/software_training_assignment/uwrt_software_trainining/software_training/msg/Software.msg new file mode 100644 index 0000000..8dd0f54 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/msg/Software.msg @@ -0,0 +1,3 @@ +float64 x_pos +float64 y_pos +float64 distance diff --git a/software_training_assignment/uwrt_software_trainining/software_training/package.xml b/software_training_assignment/uwrt_software_trainining/software_training/package.xml new file mode 100644 index 0000000..cc3cfe2 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/package.xml @@ -0,0 +1,40 @@ + + + + software_training + 0.0.0 + Software Training Package + niiquaye + Apache-2.0 + + ament_cmake + + rclcpp_components + rclcpp + std_msgs + turtlesim + rcutils + geometry_msgs + builtin_interfaces + rosidl_default_generators + rclcpp_action + + rclcpp + rclcpp_components + launch_ros + std_msgs + rcutils + turtlesim + geometry_msgs + rosidl_default_runtime + rclcpp_action + + ament_lint_auto + ament_lint_common + + rosidl_interface_packages + + + ament_cmake + + \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/src/cmd_vel_moving_turt_publisher.cpp b/software_training_assignment/uwrt_software_trainining/software_training/src/cmd_vel_moving_turt_publisher.cpp new file mode 100644 index 0000000..d2706f2 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/src/cmd_vel_moving_turt_publisher.cpp @@ -0,0 +1,35 @@ +#include + +using namespace std::chrono_literals; + +namespace composition { + +cmd_vel_moving_turt_publisher::cmd_vel_moving_turt_publisher( + const rclcpp::NodeOptions &options) + : Node("cmd_vel_moving_turt_publisher", options) { + + auto publisher_callback = [this](void) -> void { + auto message = std::make_unique(); + message->linear.x = coordinates::linear::x; + message->linear.y = coordinates::linear::y; + message->linear.z = coordinates::linear::z; + + message->angular.x = coordinates::angular::x; + message->angular.y = coordinates::angular::y; + message->angular.z = coordinates::angular::z; + + this->publisher->publish(std::move(message)); + }; + + // create the publisher + // also create a QoS object with a history/depth of 10 calls + this->publisher = this->create_publisher( + "/turtle1/cmd_vel", rclcpp::QoS(QUEUE)); + + this->timer = this->create_wall_timer(100ms, publisher_callback); +} + +} // namespace composition + +#include +RCLCPP_COMPONENTS_REGISTER_NODE(composition::cmd_vel_moving_turt_publisher) \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/src/moving_turtle_action_server.cpp b/software_training_assignment/uwrt_software_trainining/software_training/src/moving_turtle_action_server.cpp new file mode 100644 index 0000000..93b32ec --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/src/moving_turtle_action_server.cpp @@ -0,0 +1,141 @@ +#include +#include +#include + +using namespace std::chrono_literals; +using namespace std::placeholders; + +namespace composition { + +moving_turtle_action_server::moving_turtle_action_server( + const rclcpp::NodeOptions &options) + : Node("moving_turtle_action_server", options) { + + // create publisher + this->publisher = this->create_publisher( + "/moving_turtle/cmd_vel", rclcpp::QoS(QUEUE)); + + auto subscriber_callback = + [this](const turtlesim::msg::Pose::SharedPtr msg) -> void { + this->x = msg->x; + this->y = msg->y; + this->theta = msg->theta; + this->linear_velocity = msg->linear_velocity; + this->angular_velocity = msg->angular_velocity; + }; + + // create subscriber + this->subscriber = this->create_subscription( + "/moving_turtle/pose", QUEUE, subscriber_callback); + + // create action server + this->action_server = + rclcpp_action::create_server( + this, "moving_turtle_action_server", + std::bind(&moving_turtle_action_server::handle_goal, this, _1, _2), + std::bind(&moving_turtle_action_server::handle_cancel, this, _1), + std::bind(&moving_turtle_action_server::handle_accepted, this, _1) + ); +} + +rclcpp_action::GoalResponse moving_turtle_action_server::handle_goal( + const rclcpp_action::GoalUUID &uuid, + std::shared_ptr goal) { + (void)uuid; // prevents compiler warnings + RCLCPP_INFO(this->get_logger(), "Goal Received"); + return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; +} + +rclcpp_action::CancelResponse moving_turtle_action_server::handle_cancel( + const std::shared_ptr goal_handle) { + (void)goal_handle; // not needed + RCLCPP_INFO(this->get_logger(), "Received Request to cancel goal!"); + return rclcpp_action::CancelResponse::ACCEPT; +} + +void moving_turtle_action_server::handle_accepted( + const std::shared_ptr goal_handle) { + std::thread{std::bind(&moving_turtle_action_server::execute, this, _1), + goal_handle} + .detach(); +} + +void moving_turtle_action_server::execute( + const std::shared_ptr goal_handle) { + + + rclcpp::Time start_time = this->now(); + RCLCPP_INFO(this->get_logger(), "Executing Goal"); + rclcpp::Rate cycle_rate{10}; + + const auto goal = goal_handle->get_goal(); + auto result = std::make_shared(); + + float &curr_x = feedback->x_pos; + float &curr_y = feedback->y_pos; + float &curr_theta = feedback->theta_pos; + + // track physical distance + + float target_x = goal->linear_pos.x; + float target_y = goal->linear_pos.y; + float distance_to_goal = 100.0f; + + while (rclcpp::ok() && distance_to_goal > 0.1f) { + // here we update euclidian distance + distance_to_goal = std::sqrt(std::pow(target_x - this->x, 2) + std::pow(target_y - this->y, 2)); + + // check if goal has been reached + if (goal_handle->is_canceling()) { + RCLCPP_INFO(this->get_logger(), "Goal Canceled"); + rclcpp::Duration time = this->now() - start_time; + result->duration = static_cast(time.nanoseconds()); + goal_handle->canceled(std::move(result)); + return; + } + + // create message + auto message_cmd_vel = std::make_unique(); + + float angle_to_target = std::atan2(target_y - this->y, target_x - this->x); + float angle_error = angle_to_target - this->theta; + + // normalize angle to prevent wild spinning + while (angle_error > M_PI) + angle_error -= 2.0f * M_PI; + while (angle_error < -M_PI) + angle_error += 2.0f * M_PI; + + message_cmd_vel->linear.x = 1.0f * distance_to_goal; + message_cmd_vel->angular.z = 4.0f * angle_error; + + // publish message + this->publisher->publish(std::move(message_cmd_vel)); + + // compute feedback + auto feedback_msg = std::make_shared(); + feedback_msg->x_pos = distance_to_goal; + feedback_msg->y_pos = 0.0f; + feedback_msg->theta_pos = angle_error; + + goal_handle->publish_feedback(feedback_msg); + + cycle_rate.sleep(); + } // brutal problem + + // stop when loop stops + auto stop_msg = std::make_unique(); + this->publisher->publish(std::move(stop_msg)); + + if (rclcpp::ok()) { + rclcpp::Duration duration = this->now() - start_time; + result->duration = static_cast(duration.nanoseconds()); + goal_handle->succeed(std::move(result)); + RCLCPP_INFO(this->get_logger(), "Finish Executing Goal"); + } +} + +} // namespace composition + +#include +RCLCPP_COMPONENTS_REGISTER_NODE(composition::moving_turtle_action_server) \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/src/reset_moving_turtle_service.cpp b/software_training_assignment/uwrt_software_trainining/software_training/src/reset_moving_turtle_service.cpp new file mode 100644 index 0000000..28ceadb --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/src/reset_moving_turtle_service.cpp @@ -0,0 +1,67 @@ +#include +#include + +using namespace std::placeholders; +using namespace std::chrono_literals; + +namespace composition { + +reset_moving_turtle_service::reset_moving_turtle_service( + const rclcpp::NodeOptions &options) + : Node("reset_moving_turtle_service", options) { + + // create client + this->client = this->create_client( + "/moving_turtle/teleport_absolute"); + + // create service + this->service = this->create_service( + "/reset_moving_turtle", + std::bind(&reset_moving_turtle_service::service_callback, this, _1, _2)); +} + +void reset_moving_turtle_service::service_callback( + const std::shared_ptr request, + std::shared_ptr response) { + + (void)request; // request is not needed + + RCLCPP_INFO(this->get_logger(), "Starting ..."); + + // make client call to reset turtle + if (!client->wait_for_service(1s)) { + if (!rclcpp::ok()) { + RCLCPP_ERROR(this->get_logger(), "System Aborted"); + response->success = false; + return; + } + RCLCPP_INFO(this->get_logger(), "Service is not available! Exit!"); + response->success = false; + return; + } + + auto client_request = std::make_shared(); + + // fill request data + client_request->x = reset_coordinates::x; + client_request->y = reset_coordinates::y; + client_request->theta = reset_coordinates::theta; + + // create response callback + auto response_callback = + [this](rclcpp::Client::SharedFuture future) + -> void { + (void)future; // not needed + RCLCPP_INFO(this->get_logger(), "Turtle Moved"); + }; + + // send client request + auto result = client->async_send_request(client_request, response_callback); + RCLCPP_INFO(this->get_logger(), "Turtle Resetting"); + response->success = true; +} + +} // namespace composition + +#include +RCLCPP_COMPONENTS_REGISTER_NODE(composition::reset_moving_turtle_service) \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/src/spawn_turtle_nodelet.cpp b/software_training_assignment/uwrt_software_trainining/software_training/src/spawn_turtle_nodelet.cpp new file mode 100644 index 0000000..5fa3efc --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/src/spawn_turtle_nodelet.cpp @@ -0,0 +1,64 @@ +#include +#include + +using namespace std::chrono_literals; + +namespace composition { + +spawn_turtle_nodelet::spawn_turtle_nodelet(const rclcpp::NodeOptions &options) + : Node("spawn_turtle_nodelet", options) { + + // create a client that makes a request to '/spawn' service + client = this->create_client("/spawn"); + + // create client callback + timer = this->create_wall_timer( + 2s, std::bind(&spawn_turtle_nodelet::spawn_turtle, this)); + + // fill up map with contents + for (size_t i{0}; i < NUMBER_OF_TURTLES; i++) { + turtle_description.insert({turtle_names[i], turtle_bio[i]}); + } +} + +void spawn_turtle_nodelet::spawn_turtle() { + + this->timer->cancel(); + + if (!client->wait_for_service(2s)) { + if (!rclcpp::ok()) { + RCLCPP_ERROR(this->get_logger(), "Service was interupted - EXIT"); + return; + } + RCLCPP_INFO(this->get_logger(), "Waiting for service - DNE - EXIT"); + return; + } + + for (const std::string &name : turtle_names) { + + // create request + auto request = std::make_unique(); + + // fill in response + request->name = name; + request->x = turtle_description[name].x_pos; + request->y = turtle_description[name].y_pos; + request->theta = turtle_description[name].rad; + + // create a callback to call client + auto callback = + [this](rclcpp::Client::SharedFuture response) + -> void { + RCLCPP_INFO(this->get_logger(), "Turtle Created: %s", + response.get()->name.c_str()); + }; + + // send request + auto result = client->async_send_request(std::move(request), callback); + } +} + +} // namespace composition + +#include +RCLCPP_COMPONENTS_REGISTER_NODE(composition::spawn_turtle_nodelet) \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/src/turtle_neutralize.cpp b/software_training_assignment/uwrt_software_trainining/software_training/src/turtle_neutralize.cpp new file mode 100644 index 0000000..65c590f --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/src/turtle_neutralize.cpp @@ -0,0 +1,47 @@ +#include + +using namespace std::chrono_literals; + +namespace composition { + +turtle_service_request_node::turtle_service_request_node( + const rclcpp::NodeOptions &options) + : Node("turtle_service_request_node", options) { + client = this->create_client("/kill"); + timer = this->create_wall_timer( + 2s, std::bind(&turtle_service_request_node::kill, this)); +} + +void turtle_service_request_node::kill() { + + this->timer->cancel(); + + if (!client->wait_for_service(2s)) { + if (!rclcpp::ok()) { + RCLCPP_ERROR(this->get_logger(), + "Interrupted while waiting for service. Exiting!"); + return; + } + RCLCPP_INFO(this->get_logger(), "Service not available after waiting"); + return; + } + + for (std::string &name : turtle_names) { + auto request = std::make_shared(); + request->name = name; + + auto callback = + [this](rclcpp::Client::SharedFuture response) + -> void { + (void)response; + RCLCPP_INFO(this->get_logger(), "Turtle Killed"); + }; + + auto result = client->async_send_request(request, callback); + } +} + +} // namespace composition + +#include +RCLCPP_COMPONENTS_REGISTER_NODE(composition::turtle_service_request_node) \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/src/turtle_publisher.cpp b/software_training_assignment/uwrt_software_trainining/software_training/src/turtle_publisher.cpp new file mode 100644 index 0000000..a88c221 --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/src/turtle_publisher.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +using namespace std::chrono_literals; + +namespace composition { + +turtle_publisher::turtle_publisher(const rclcpp::NodeOptions &options) + : Node("turtle_publisher", options) { + + auto stationary_turt_callback = + [this](const turtlesim::msg::Pose::SharedPtr msg) -> void { + this->x_stationary_turt = msg->x; + this->y_stationary_turt = msg->y; + }; + + auto moving_turt_callback = + [this](const turtlesim::msg::Pose::SharedPtr msg) -> void { + this->x_moving_turt = msg->x; + this->y_moving_turt = msg->y; + }; + + auto publisher_callback = [this](void) -> void { + double position_x{std::abs(this->x_stationary_turt - this->x_moving_turt)}; + double position_y{std::abs(this->y_stationary_turt - this->y_moving_turt)}; + + auto msg = std::make_unique(); + msg->x_pos = position_x; + msg->y_pos = position_y; + msg->distance = std::sqrt((position_x * position_x) + (position_y * position_y)); + + this->publisher->publish(std::move(msg)); + }; + + callbacks = + this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive); + + auto callback_option = rclcpp::SubscriptionOptions(); + callback_option.callback_group = callbacks; + + callback_option.topic_stats_options.state = + rclcpp::TopicStatisticsState::Enable; + + std::string s_name("/statistics/"); + std::string node_name(this->get_name()); + std::string stat_name = s_name + node_name; + callback_option.topic_stats_options.publish_topic = stat_name.c_str(); + + stationary_turt_sub = this->create_subscription( + "/stationary_turtle/pose", QUEUE, stationary_turt_callback, + callback_option); + + moving_turt_sub = this->create_subscription( + "/moving_turtle/pose", QUEUE, moving_turt_callback, callback_option); + + publisher = this->create_publisher( + "/difference", QUEUE); + + timer = this->create_wall_timer(3s, publisher_callback, callbacks); +} + +} // namespace composition + +#include +RCLCPP_COMPONENTS_REGISTER_NODE(composition::turtle_publisher) \ No newline at end of file diff --git a/software_training_assignment/uwrt_software_trainining/software_training/srv/Software.srv b/software_training_assignment/uwrt_software_trainining/software_training/srv/Software.srv new file mode 100644 index 0000000..d857c6a --- /dev/null +++ b/software_training_assignment/uwrt_software_trainining/software_training/srv/Software.srv @@ -0,0 +1,3 @@ + +--- +bool success diff --git a/three_turtles.png b/three_turtles.png new file mode 100644 index 0000000000000000000000000000000000000000..844edcd95c6b442a69b00fd9aa918db69d20b8d5 GIT binary patch literal 11191 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%0AZpA_|uX2z8Kg%eR8(=%|`nEsQE#YRKT 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 z%0DoA_gz3GI(}MCcy{za1sW{lY6;r2i0finUCogK(YS3@n}-ijP%}0@0eFSfn(lCS z21~jRf4o150-=B=vP0#i<);#}3p3K`M+#Li6WySQAKE*tm)IvZWzxnq(M`!IWBw$W zv%$eVeYgzXz#Jyl?XtflNG+>1f7Nnaoc}26df^k3Zn3k@$L~oUJq(lyN*5=rEzPy* zvZyaI2x|XPav&Zpv)r)a<^p6tdZzb&ETvYqc(J&)2c6f@hwh=@RjIoAdJ|+-(IeBi z?}n%ddoPv5b4jc|FG$R!X1R6kX7|FOJxj3}3f2g9i(jgRGl{W@MW19@imM`q7XrQ! zNWBtOlhqg>s}K;|kFs4g74TC!Dbp$WAIfb7@i(z?KuIG#tV0$fw{*4hN3OvuHp%A& z;jN=%6X(}q%XyzAZz{9?so(0+?C~h}N-!U?NGs_;28fbK6{PP*S(5V`oM{;@*|Mvj z$?p7<53Amc0$BW`Fym4S6rP6FF))c@-GxfnOS(yV0Q@9U>1hfeOs$jCT?OM z``F<51<&whJ<<7s^O(9IdNCn))`fYgk51pg-qBFA(gQy)BUMsjuRdsaaP)p1g~^Fv zKd6de$`Xkb8d4quDeZpiWtgh2x_Z{3a@efDOCrM0PsO1r?b&Kyke%z(6yCju6;R>n z!kl6SBrXx#UeUpxCM^>0QML2Y`O+7=cI5MQEBc@h3xvBF+MT|C&qCVjIk1p9J9UG~ z78Z}t77~9f1|1P*_~^B2tCEO*vyI`x%uK7)wad!=<#I7!(MmcK5-(4~@0ZMu$^Gl-}V;- z*ioNfh>}@iLp!rk?@adlr7HXzpTTu>AC+CP9m^h}=*Q1CJF1> z=C!9Fa(}sxD^TI-8G!tb&s2ES9pW3lcS9N`1&1AVl%_qN+9Isv7k`tup95Q!=^}k7 zo~T$rg`b~MNZpz<_c8v-`ffX;{sE9ayJ?cK5le8fcOrb*`2%5qSTTE~!ulDOgv2sd z9)@)>k*{MH_)q=e;kVFDS!!a|on7OPcVE9rL1<^;HwEo~KXF;=(A3$&dlJ7X2RS{T zyeT@cytcnjoOniAaV^{^UHM#d?dp6Tlk4}I7D%5MgntSOmTk>V(p4aSUCTF=#TB!{HMk-%TssW&AF6qdqarT2^mLUP z4sg(S;z4e64MwDt@?r@s^AROEU~=bB4R+)LIw0zn0=k9m8!sl$;8OhK8lc zh2cd5f^gQy72Dk&qQnw)?PI4eL(8g^hd@&|htC={`zF*KrdFjmrKcL!cbi{cGLheT zg`R`V*Mj;KHp}cxfE}$mE0$y{1DLJK$Sd+2i@XZpMlDAteL2j(*b-= zaR`K~XoXe?emTc5*o&t(SJnFk4GzY`y7s-4Hs%K5FV^7H9?m5enk(K zYM`{6Y3kk)m+NHX%NoB;hsaHufjenHLR|;l?C?+56*1{0uM}%{xulTpM(QD@3ply=AZ`+8UUmu9a3WM3qSED;*bi;WV`2!`-Aq3~7mRejsGLLLw!~fRe>QZ1P5Rvv zLU4N#$J(%38H3WRflEu-L*e4a<*Tgte1_M$R?_mW7dm#xZHli}vK|wEK+m3bov)GL zvA}FN_-Aqw(;N&dS9o|_!WtBtnicuAk&Asy8~(G911ik~f{0b4{Iu%NPqL9b9J+?A z{wWsopa7vT>-FG@jVQlpTY*0SCBiQ}G6S}qEftYMCj(AY|5=MoVEf(+jm^s=K1Dlu zdm)Pv)%%6J@+-}^8!o+Q*XEu*MoDTn>jqFl@mrAs>Wl5)^r;0Dy#TokBqV{|qo0S# zIW<>f?Rdm981}V7s;#ZBFUUhOy_{tgsIVdm$6(F$M0VG3mLdnt$zsTUMS1(tyrQ-tK|Jr z&G}Cr`VTO=KU|jCJVAW2UBCd>dHy_I%E@q$uQ+F!#axqSes!Ro`C@~6NmbMMkDCwR zJgHm9*uwMzN1lsb7r)Xm7~d*12~jgxD%m)h0T-_sJ_^0sjk`Alb{;$grh6~^@`}G9a?tlAD)W6`l z?*FOuUtNIy-*v$McO(3->JYIMEbf=mS#XC9O`+_~?bt&gb(fF%&cQAe45JvFz?W?y N@OuyM=BYe=`){B_85aNm literal 0 HcmV?d00001 diff --git a/turtle_move.png b/turtle_move.png new file mode 100644 index 0000000000000000000000000000000000000000..3fee998e83f4ec30b4edd60b69eb63bfe967f664 GIT binary patch literal 11879 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

+@?~60WmhTU%Qz1|N0( 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 bDZoRN<5U8RLn!#<0|LFTsZ^|B5%fO*YClm$ literal 0 HcmV?d00001 From dda393ade62ce5049d97ed6806894590acf971fd Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Wed, 16 Oct 2019 09:32:46 -0700 Subject: [PATCH 2/2] Initial commit --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..77bcc77 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# software_challenge \ No newline at end of file