77#include " behaviortree_cpp/loggers/groot2_publisher.h"
88#include " behaviortree_cpp/xml_parsing.h"
99
10+ #include < cstdlib>
11+ #include < string>
12+ #include < vector>
13+
1014/* * We are using the same example in Tutorial 5,
1115 * But this time we also show how to connect
1216 */
@@ -28,7 +32,22 @@ BT_JSON_CONVERTER(Position2D, pos)
2832 add_field (" y" , &pos.y );
2933}
3034
35+ struct Waypoint
36+ {
37+ std::string name;
38+ Position2D position;
39+ double speed = 1.0 ;
40+ };
41+
42+ BT_JSON_CONVERTER (Waypoint, wp)
43+ {
44+ add_field (" name" , &wp.name );
45+ add_field (" position" , &wp.position );
46+ add_field (" speed" , &wp.speed );
47+ }
48+
3149// Simple Action that updates an instance of Position2D in the blackboard
50+ // and also outputs vectors to reproduce Groot2 issue #55/#77
3251class UpdatePosition : public BT ::SyncActionNode
3352{
3453public:
@@ -41,16 +60,63 @@ class UpdatePosition : public BT::SyncActionNode
4160 _pos.x += 0.2 ;
4261 _pos.y += 0.1 ;
4362 setOutput (" pos" , _pos);
63+
64+ // Vectors that trigger Groot2 issue #55/#77 (crash when viewing blackboard)
65+ std::vector<double > doubles = { 1.1 , 2.2 , 3.3 , 4.4 , 5.5 };
66+ setOutput (" vec_double" , doubles);
67+
68+ std::vector<std::string> strings = { " hello" , " world" , " test" };
69+ setOutput (" vec_string" , strings);
70+
71+ // Helper to generate random offset [-range, +range]
72+ auto randOffset = [](double range) {
73+ return (static_cast <double >(std::rand ()) / RAND_MAX - 0.5 ) * 2.0 * range;
74+ };
75+
76+ // Vector of custom types - alternate between 2 and 3 waypoints
77+ std::vector<Waypoint> waypoints;
78+ if (_execution_count % 2 == 0 )
79+ {
80+ // Even executions: 2 waypoints
81+ waypoints = {
82+ { " start" , { randOffset (5.0 ), randOffset (5.0 ) }, 1.0 + randOffset (0.3 ) },
83+ { " goal" ,
84+ { 100.0 + randOffset (10.0 ), 50.0 + randOffset (10.0 ) },
85+ 0.5 + randOffset (0.2 ) },
86+ };
87+ }
88+ else
89+ {
90+ // Odd executions: 3 waypoints
91+ waypoints = {
92+ { " start" , { randOffset (5.0 ), randOffset (5.0 ) }, 1.0 + randOffset (0.3 ) },
93+ { " checkpoint" ,
94+ { 50.0 + randOffset (15.0 ), 25.0 + randOffset (15.0 ) },
95+ 2.0 + randOffset (0.5 ) },
96+ { " goal" ,
97+ { 100.0 + randOffset (10.0 ), 50.0 + randOffset (10.0 ) },
98+ 0.5 + randOffset (0.2 ) },
99+ };
100+ }
101+ setOutput (" waypoints" , waypoints);
102+ _execution_count++;
103+
44104 return BT::NodeStatus::SUCCESS;
45105 }
46106
47107 static BT::PortsList providedPorts ()
48108 {
49- return { BT::OutputPort<Position2D>(" pos" ) };
109+ return {
110+ BT::OutputPort<Position2D>(" pos" ),
111+ BT::OutputPort<std::vector<double >>(" vec_double" , " Vector of doubles" ),
112+ BT::OutputPort<std::vector<std::string>>(" vec_string" , " Vector of strings" ),
113+ BT::OutputPort<std::vector<Waypoint>>(" waypoints" , " Vector of waypoints" ),
114+ };
50115 }
51116
52117private:
53118 Position2D _pos = { 0 , 0 };
119+ int _execution_count = 0 ;
54120};
55121
56122// clang-format off
@@ -61,7 +127,7 @@ static const char* xml_text = R"(
61127 <BehaviorTree ID="MainTree">
62128 <Sequence>
63129 <Script code="door_open:=false" />
64- <UpdatePosition pos="{pos_2D}" />
130+ <UpdatePosition pos="{pos_2D}" vec_double="{doubles}" vec_string="{strings}" waypoints="{waypoints}" />
65131 <Fallback>
66132 <Inverter>
67133 <IsDoorClosed/>
@@ -105,6 +171,7 @@ int main()
105171
106172 // Add this to allow Groot2 to visualize your custom type
107173 BT::RegisterJsonDefinition<Position2D>();
174+ BT::RegisterJsonDefinition<Waypoint>();
108175
109176 auto tree = factory.createTree (" MainTree" );
110177
0 commit comments