Esempio n. 1
0
 def test_brain_variable_weight(self, sim_mock):
     config.brain_root = FooBrain()
     dev = LeakyIntegrator(weight=config.brain_root.foo)
     start_new_tf_manager()
     config.active_node.brain_adapter = MockBrainCommunicationAdapter()
     self.assertTrue(sim_mock().Population.called)
     self.assertTrue(sim_mock().initialize.called)
     self.assertDictEqual(
         dev.get_parameters(), {
             'v_thresh': float('inf'),
             'cm': 1.0,
             'tau_m': 10.0,
             'tau_syn_E': 2.,
             'tau_syn_I': 2.,
             'v_rest': 0.0,
             'v_reset': 0.0,
             'tau_refrac': 0.1,
             'i_offset': 0.0,
             'connector': sim_mock().AllToAllConnector(),
             'weight': 1.0,
             'delay': 0.1,
             'source': None,
             'receptor_type': 'excitatory',
             'synapse_type': sim_mock().StaticSynapse(),
             'label': None,
             'rng': None
         })
Esempio n. 2
0
    def test_map_neuron_wrong_parameter_fails(self):
        nrp.start_new_tf_manager()

        # This test checks if the initialization fails as expected, because "neuronX" cannot be mapped to a parameter
        with self.assertRaises(Exception):
            @nrp.MapSpikeSink("neuronX", [1, 2, 3], nrp.leaky_integrator_exp)
            @nrp.NeuronMonitor(nrp.brain.foo, nrp.spike_recorder)
            def example_monitor(t, neuron1):
                return neuron1.voltage > 20
Esempio n. 3
0
    def test_map_neuron_wrong_parameter_fails(self):
        nrp.start_new_tf_manager()

        # This test checks if the initialization fails as expected, because "neuronX" cannot be mapped to a parameter
        with self.assertRaises(Exception):

            @nrp.MapSpikeSink("neuronX", [1, 2, 3], nrp.leaky_integrator_exp)
            @nrp.Neuron2Robot(Husky.RightArm.pose)
            def right_arm(t, neuron1):
                return neuron1.voltage
Esempio n. 4
0
    def setUp(self):
        nrp.start_new_tf_manager()
        brain = MockBrainCommunicationAdapter()
        robot = MockRobotCommunicationAdapter()

        nrp.set_nest_adapter(brain)
        nrp.set_robot_adapter(robot)

        brain.actors = MockPopulation(range(0, 60))
        brain.sensors = MockPopulation(range(45, 645))
        config.brain_root = brain
    def __load_cle(self, roscontrol, roscomm, braincontrol, braincomm,
                   brain_file_path, neurons_config, externalmodulearray,
                   robot_poses, models, lights):
        """
        Load the ClosedLoopEngine and initializes all interfaces

        :param roscontrol Robot Control Adapter to use
        :param roscomm Robot Communication Adapter to use
        :param braincontrol Brain Control Adapter to use
        :param braincomm Brain Communication Adapter to use
        :param brain_file_path Accessible path to brain file
        :param neurons_config Neuron configuration specified in the BIBI
        :param robot_post Initial robot pose
        :param models Initial models loaded into the environment
        :param lights Initial lights loaded into the environment
        """

        # Needed in order to cleanup global static variables
        self._notify("Connecting brain simulator to robot")
        tfm.start_new_tf_manager()

        # Create transfer functions manager
        tfmanager = tfm.config.active_node

        # set adapters
        tfmanager.robot_adapter = roscomm
        tfmanager.brain_adapter = braincomm

        # integration timestep between simulators, convert from ms to s (default to CLE value)
        timestep = (ClosedLoopEngine.DEFAULT_TIMESTEP
                    if self.sim_config.timestep is None else
                    self.sim_config.timestep)

        roscontrol.set_robots(self.robotManager.get_robot_dict())

        # initialize CLE
        self._notify("Initializing CLE")

        cle = DeterministicClosedLoopEngine(roscontrol, roscomm, braincontrol,
                                            braincomm, tfmanager,
                                            externalmodulearray, timestep)

        if brain_file_path:
            cle.initialize(brain_file_path, **neurons_config)
        else:
            cle.initialize()

        # Set initial pose
        cle.initial_robot_poses = robot_poses
        # Set initial models and lights
        cle.initial_models = models
        cle.initial_lights = lights

        return cle
Esempio n. 6
0
    def test_parameter_not_parsable_fails(self):
        nrp.start_new_tf_manager()

        # This test checks if the initialization fails as expected, because there is no mapping for parameter "neuron1"

        @nrp.NeuronMonitor(nrp.brain.foo, nrp.spike_recorder)
        def example_monitor(t, neuronA):
            return neuronA.voltage > 20

        self.init_adapters()

        self.assertRaises(Exception, nrp.initialize)
Esempio n. 7
0
    def test_parameter_not_parsable_fails(self):
        nrp.start_new_tf_manager()

        # This test checks if the initialization fails as expected, because there is no mapping for parameter "neuron1"

        @nrp.Neuron2Robot(Husky.RightArm.pose)
        def right_arm(t, neuronA):
            return neuronA.voltage

        self.init_adapters()

        self.assertRaises(Exception, nrp.initialize)
Esempio n. 8
0
    def test_tf_delete(self, mock_cleanup):

        nrp.start_new_tf_manager()

        brain = MockBrainCommunicationAdapter()
        robot = MockRobotCommunicationAdapter()
        config.active_node.brain_adapter = brain

        nrp.set_nest_adapter(brain)
        nrp.set_robot_adapter(robot)

        @nrp.MapCSVRecorder("recorder1", filename = "1", headers=["Name", "time", "Position"])
        @nrp.MapSpikeSink("neuron0", nrp.brain.actors[slice(0, 2, 1)], nrp.leaky_integrator_alpha,
                                v_rest=1.0, updates=[(1.0, 0.3)])
        @nrp.Neuron2Robot(Husky.RightArm.pose)
        def right_arm(t, neuron0, recorder1):
            return neuron0.voltage * 1.345


        @nrp.MapCSVRecorder("recorder2", filename = "2", headers=["Name", "time", "Position"])
        @nrp.MapSpikeSink("neuron1", nrp.brain.actors[slice(2, 4, 1)], nrp.leaky_integrator_alpha,
                                updates=[(1.0, 0.4)], v_rest=1.0)
        @nrp.MapSpikeSink("neuron2", nrp.brain.actors[slice(4, 6, 1)], nrp.leaky_integrator_alpha,
                                updates=[(1.0, 0.4)], v_rest=1.0)
        @nrp.Neuron2Robot(Husky.LeftArm.twist)
        def left_arm_tw(t, neuron1, neuron2, recorder2):
            if neuron1.voltage < 0.678:
                if neuron2.voltage > 0.345:
                    return 0.756
                else:
                    return 1.123
            else:
                if neuron2.voltage < 0.789:
                    return 0.632
                else:
                    return 0.256

        nrp.initialize("MyTransferFunctions")

        # Delete an existing transfer function
        self.assertEqual(2, len(nrp.get_transfer_functions()))
        self.assertEqual(True, nrp.delete_transfer_function("left_arm_tw"))
        self.assertEqual(1, len(nrp.get_transfer_functions()))
        self.assertEqual(mock_cleanup.call_count, 1)
        # Try to delete it again
        self.assertEqual(False, nrp.delete_transfer_function("left_arm_tw"))
        self.assertEqual(1, len(nrp.get_transfer_functions()))
        # Delete another existing transfer function
        self.assertEqual(True, nrp.delete_transfer_function("right_arm"))
        self.assertEqual(0, len(nrp.get_transfer_functions()))
        self.assertEqual(0, len(config.active_node.brain_adapter.detector_devices))
        self.assertEqual(mock_cleanup.call_count, 2)
Esempio n. 9
0
    def __load_cle(self, roscontrol, roscomm, braincontrol, braincomm,
                   brain_file_path, neurons_config, robot_pose, models,
                   lights):
        """
        Load the ClosedLoopEngine and initializes all interfaces

        :param roscontrol Robot Control Adapter to use
        :param roscomm Robot Communication Adapter to use
        :param braincontrol Brain Control Adapter to use
        :param braincomm Brain Communication Adapter to use
        :param brain_file_path Accessible path to brain file
        :param neurons_config Neuron configuration specified in the BIBI
        :param robot_post Initial robot pose
        :param models Initial models loaded into the environment
        :param lights Initial lights loaded into the environment
        """

        # Needed in order to cleanup global static variables
        self._notify("Connecting brain simulator to robot")
        nrp.start_new_tf_manager()

        # Create transfer functions manager
        tfmanager = nrp.config.active_node

        # set adapters
        tfmanager.robot_adapter = roscomm
        tfmanager.brain_adapter = braincomm

        # Import dependencies
        for dep in self.__dependencies:
            importlib.import_module(dep[:dep.rfind('.')])

        # integration timestep between simulators, convert from ms to s
        # (default to CLE value)
        timestep = ClosedLoopEngine.DEFAULT_TIMESTEP
        if self.bibi.timestep is not None:
            timestep = float(self.bibi.timestep) / 1000.0

        # initialize CLE
        self._notify("Initializing CLE")
        cle = DeterministicClosedLoopEngine(roscontrol, roscomm, braincontrol,
                                            braincomm, tfmanager, timestep)
        cle.initialize(brain_file_path, **neurons_config)

        # Set initial pose
        cle.initial_robot_pose = robot_pose
        # Set initial models and lights
        cle.initial_models = models
        cle.initial_lights = lights

        return cle
Esempio n. 10
0
    def test_no_tf_decorator_works(self):
        nrp.start_new_tf_manager()

        @nrp.MapSpikeSink("neuron", [1, 2, 3], nrp.leaky_integrator_alpha)
        def tf_sink_without_decorator(t, neuron):
            pass

        self.assertIsInstance(tf_sink_without_decorator, nrp.Neuron2Robot)

        @nrp.MapRobotPublisher("topic", "/a/topic")
        def tf_publisher_no_decorator(t, topic):
            pass

        self.assertIsInstance(tf_publisher_no_decorator, nrp.Neuron2Robot)
    def test_no_tf_decorator(self):
        nrp.start_new_tf_manager()

        @nrp.MapSpikeSource("neuron", [1, 2, 3], nrp.poisson)
        def tf_source_without_decorator(t, neuron):
            pass

        self.assertIsInstance(tf_source_without_decorator, nrp.Robot2Neuron)

        @nrp.MapRobotSubscriber("topic", "/a/topic")
        def tf_subscriber_without_decorator(t, topic):
            pass

        self.assertIsInstance(tf_subscriber_without_decorator, nrp.Robot2Neuron)
Esempio n. 12
0
    def test_set_flawed_tf(self):
        nrp.start_new_tf_manager()

        tf_name = "tf_name"
        tf_src = "def tf_name(self): pass"
        tf_error = Exception()

        nrp.set_flawed_transfer_function(tf_src, tf_name, tf_error)
        #  check if correctly added
        flawed_tf = nrp.get_flawed_transfer_function(tf_name)

        self.assertEqual(flawed_tf.name, tf_name)
        self.assertEqual(flawed_tf.source, tf_src)
        self.assertTrue(flawed_tf.error is tf_error)
Esempio n. 13
0
    def test_delete_flawed_tf(self):
        nrp.start_new_tf_manager()

        tf_name = "tf_name"
        tf_src = "def tf_name(self): pass"
        tf_error = Exception()

        nrp.set_flawed_transfer_function(tf_src, tf_name, tf_error)

        result = nrp.delete_flawed_transfer_function(tf_name)
        self.assertTrue(result)

        # can't find it anymore
        self.assertIsNone(nrp.get_flawed_transfer_function(tf_name))
Esempio n. 14
0
    def test_tf_publish_error(self, logcapture):

        nrp.start_new_tf_manager()

        brain = MockBrainCommunicationAdapter()
        config.active_node.brain_adapter = brain
        config.active_node.publish_error_callback = MagicMock(return_value=None)

        @nrp.MapSpikeSink("neuron0", nrp.brain.actors[slice(0, 2, 1)], nrp.leaky_integrator_alpha,
                                v_rest=1.0, updates=[(1.0, 0.3)])
        @nrp.Neuron2Robot(Husky.RightArm.pose)
        def right_arm(t, neuron0):
            raise Exception('foo')

        tf = nrp.get_transfer_function('right_arm')
        self.assertRaises(TFException, tf.run, 0.1)
Esempio n. 15
0
    def test_spike_recorder_monitor(self):
        nrp.start_new_tf_manager()
        self.init_adapters()

        @nrp.NeuronMonitor(nrp.brain.foo, nrp.spike_recorder)
        def my_monitor(t):
            return True

        nrp.initialize("test")
        my_monitor.run(42.0)
        msg = my_monitor.publisher.sent[-1]
        self.assertIsInstance(msg, SpikeEvent)
        self.assertEqual(msg.simulationTime, 42.0)
        self.assertEqual(msg.monitorName, "my_monitor")
        self.assertEqual(msg.neuronCount, 42)
        self.assertEqual(len(msg.spikes), 0)

        my_monitor.unregister()
        self.assertIsNone(my_monitor.device)
Esempio n. 16
0
    def test_population_rate_monitor(self):

        nrp.start_new_tf_manager()
        self.init_adapters()

        @nrp.NeuronMonitor(nrp.brain.foo, nrp.population_rate)
        def my_monitor(t):
            return True

        nrp.initialize("test")
        my_monitor.run(42.0)
        msg = my_monitor.publisher.sent[-1]
        self.assertIsInstance(msg, SpikeRate)
        self.assertEqual(msg.simulationTime, 42.0)
        self.assertEqual(msg.monitorName, "my_monitor")
        self.assertEqual(msg.rate, my_monitor.device.rate)

        my_monitor.unregister()
        self.assertIsNone(my_monitor.device)
Esempio n. 17
0
    def test_tf_source(self):
        nrp.start_new_tf_manager()
        brain = MockBrainCommunicationAdapter()
        robot = MockRobotCommunicationAdapter()

        @nrp.MapSpikeSink("neuron0", nrp.brain.actors[slice(0, 2, 1)], nrp.leaky_integrator_alpha,
                                v_rest=1.0, updates=[(1.0, 0.3)])
        @nrp.Neuron2Robot(Husky.RightArm.pose)
        def right_arm(t, neuron0):
            return neuron0.voltage * 1.345

        @nrp.Robot2Neuron()
        def transform_camera(t, camera, camera_device):
            if camera.changed:
                camera_device.inner.amplitude = 42.0

        expected_source_n2r = """@nrp.MapSpikeSink("neuron0", nrp.brain.actors[slice(0, 2, 1)], nrp.leaky_integrator_alpha,
                        v_rest=1.0, updates=[(1.0, 0.3)])
@nrp.Neuron2Robot(Husky.RightArm.pose)
def right_arm(t, neuron0):
    return neuron0.voltage * 1.345
"""
        expected_source_r2n = """@nrp.Robot2Neuron()
def transform_camera(t, camera, camera_device):
    if camera.changed:
        camera_device.inner.amplitude = 42.0
"""

        loaded_source_n2r = config.active_node.n2r[0].source
        self.assertEqual(loaded_source_n2r, expected_source_n2r)

        loaded_source_r2n = config.active_node.r2n[0].source
        self.assertEqual(loaded_source_r2n, expected_source_r2n)

        loaded_source_n2r_and_r2n = [tf.source for tf in nrp.get_transfer_functions()]
        self.assertIn(expected_source_n2r, loaded_source_n2r_and_r2n)
        self.assertIn(expected_source_r2n, loaded_source_n2r_and_r2n)
        self.assertEqual(2, len(loaded_source_n2r_and_r2n))
Esempio n. 18
0
    def test_all_right(self):

        nrp.start_new_tf_manager()

        brain = MockBrainCommunicationAdapter()
        robot = MockRobotCommunicationAdapter()

        @nrp.MapSpikeSink("neuron0", nrp.brain.actors[slice(0, 2, 1)], nrp.leaky_integrator_alpha,
                                v_rest=1.0, updates=[(1.0, 0.3)])
        @nrp.Neuron2Robot(Husky.RightArm.pose)
        def right_arm(t, neuron0):
            return neuron0.voltage * 1.345

        @nrp.MapSpikeSink("neuron1", nrp.brain.actors[slice(2, 4, 1)], nrp.leaky_integrator_alpha,
                                updates=[(1.0, 0.4)], v_rest=1.0)
        @nrp.MapSpikeSink("neuron2", nrp.brain.actors[slice(4, 6, 1)], nrp.leaky_integrator_alpha,
                                updates=[(1.0, 0.4)], v_rest=1.0)
        @nrp.Neuron2Robot(Husky.LeftArm.twist)
        def left_arm_tw(t, neuron1, neuron2):
            if neuron1.voltage < 0.678:
                if neuron2.voltage > 0.345:
                    return 0.756
                else:
                    return 1.123
            else:
                if neuron2.voltage < 0.789:
                    return 0.632
                else:
                    return 0.256

        # Here is an example of a transfer function mapping robot sensor data to spikes
        # As the image processing is a common task, this is done through a specialized
        # device in the neuronal simulator. However, this device might not be mapped to
        # physical Nest device, but do some processing internally and use a less specialized
        # device type internally
        @nrp.MapRobotSubscriber("camera", Husky.Eye.camera)
        @nrp.MapSpikeSource("camera_device", nrp.brain.sensors[slice(0, 600, 1)],
                                TestDevice())
        @nrp.Robot2Neuron()
        def transform_camera(t, camera, camera_device):
            if camera.changed:
                camera_device.inner.amplitude = 42.0

        nrp.set_nest_adapter(brain)
        nrp.set_robot_adapter(robot)

        brain.__dict__["actors"] = MockPopulation(range(0, 60))
        brain.__dict__["sensors"] = MockPopulation(range(45, 645))
        config.brain_root = brain

        nrp.initialize("MyTransferFunctions")

        husky_right_arm = right_arm.topic
        husky_left_arm = left_arm_tw.topic

        camera = transform_camera.camera
        camera_device = transform_camera.camera_device

        brain.refresh_buffers(0.5)
        robot.refresh_buffers(0.5)
        config.active_node.run_neuron_to_robot(0.5)
        config.active_node.run_robot_to_neuron(0.5)

        camera.value = "Definitely not an image"

        brain.refresh_buffers(1.5)
        robot.refresh_buffers(1.5)
        config.active_node.run_neuron_to_robot(1.5)
        config.active_node.run_robot_to_neuron(1.5)

        assert isinstance(husky_right_arm, MockPublishedTopic)
        assert isinstance(husky_left_arm, MockPublishedTopic)

        assert len(husky_right_arm.sent) == 2
        assert len(husky_left_arm.sent) == 2

        assert husky_right_arm.sent[0] == 1.345
        assert husky_right_arm.sent[1] == 1.345 * 0.3

        assert husky_left_arm.sent[0] == 0.256
        assert husky_left_arm.sent[1] == 0.756

        assert camera_device.inner.amplitude == 42.0

        config.active_node.reset()
Esempio n. 19
0
 def setUp(self):
     self.bcm = MockBrainCommunicationAdapter()
     self.rcm = MockRobotCommunicationAdapter()
     nrp.start_new_tf_manager()
     nrp.config.brain_root = TestBrain()
     self.tfm = nrp.config.active_node
Esempio n. 20
0
    def test_tf_set(self):
        nrp.start_new_tf_manager()
        brain = MockBrainCommunicationAdapter()
        robot = MockRobotCommunicationAdapter()
        config.active_node.brain_adapter = brain
        config.active_node.robot_adapter = robot
        config.active_node.initialize_tf = MagicMock(return_value=None)

        @nrp.MapSpikeSink("neuron0", nrp.brain.actors[slice(0, 2, 1)], nrp.leaky_integrator_alpha,
                                v_rest=1.0, updates=[(1.0, 0.3)]
        )
        @nrp.Neuron2Robot(Husky.RightArm.pose)
        def right_arm(t, neuron0):
            return neuron0.voltage * 1.345

        @nrp.Robot2Neuron()
        def transform_camera(t, camera, camera_device):
            if camera.changed:
                camera_device.inner.amplitude = 42.0

        tf_n2r = """@nrp.MapSpikeSink("neuron1", nrp.brain.actors[slice(0, 2, 1)], nrp.leaky_integrator_alpha,
                        v_rest=1.0, updates=[(3.0, 0.1)])
@nrp.Neuron2Robot()
def right_arm(t, neuron1):
    return neuron1.voltage * 2.345
"""
        tf_r2n = """@nrp.MapRobotSubscriber("camera", Topic('/husky/camera', sensor_msgs.msg.Image))
@nrp.Robot2Neuron()
def transform_camera(t, camera):
    if camera.changed:
        pass
"""
        nrp.delete_transfer_function('right_arm')
        nrp.delete_transfer_function('transform_camera')
        nrp.set_transfer_function(tf_n2r, tf_n2r, 'right_arm')
        nrp.set_transfer_function(tf_r2n, tf_r2n, 'transform_camera')
        self.assertEqual(config.active_node.initialize_tf.call_count, 2)

        loaded_source_n2r_and_r2n = [tf.source for tf in nrp.get_transfer_functions()]
        self.assertIn(tf_n2r, loaded_source_n2r_and_r2n)
        self.assertIn(tf_r2n, loaded_source_n2r_and_r2n)
        number_of_tf = len(loaded_source_n2r_and_r2n)
        self.assertEqual(2, number_of_tf)

        source_update_flags = [tf.updated for tf in nrp.get_transfer_functions()]
        self.assertEqual([True] * number_of_tf, source_update_flags)

        fancy_tf = "def it_wont_work():\n return None"
        self.assertRaises(TFLoadingException, nrp.set_transfer_function, fancy_tf, fancy_tf, "it_wont_work")
        try:
            nrp.set_transfer_function(fancy_tf, fancy_tf, 'it_wont_work')
        except TFLoadingException as e:
            self.assertIn('no decorator', e.message)
            self.assertEqual('it_wont_work', e.tf_name)

        funny_tf = """@nrp.Robot2Neuron()
def transform_camera(t, camera, camera_device):
    if camera.changed:
        return Nothing!
"""
        self.assertRaises(TFLoadingException, nrp.set_transfer_function, funny_tf, funny_tf, "it_cant_work")
        try:
            nrp.set_transfer_function(funny_tf, funny_tf, 'it_cant_work')
        except TFLoadingException as e:
            self.assertIn('invalid syntax', e.message)
            self.assertIn('line 4', e.message)
            self.assertEqual('it_cant_work', e.tf_name)
 def setUp(self):
     nrp.start_new_tf_manager()
Esempio n. 22
0
 def test_brain_source(self):
     nrp.start_new_tf_manager()
     config.brain_source = "some source"
     self.assertEqual(config.brain_source, nrp.get_brain_source())
Esempio n. 23
0
 def setUp(self):
     nrp.start_new_tf_manager()
     brain = MockBrainCommunicationAdapter()
     robot = MockRobotCommunicationAdapter()
     config.active_node.brain_adapter = brain
     config.active_node.robot_adapter = robot
Esempio n. 24
0
    def test_all_right(self):

        nrp.start_new_tf_manager()

        brain = MockBrainCommunicationAdapter()
        robot = MockRobotCommunicationAdapter()

        @nrp.MapSpikeSink("neuron0",
                          nrp.map_neurons(range(0, 2),
                                          lambda i: nrp.brain.actors[i]),
                          nrp.leaky_integrator_alpha,
                          v_rest=1.0,
                          updates=[(1.0, [0.3, 0.0])])
        @nrp.Neuron2Robot(Husky.RightArm.pose)
        def right_arm(t, neuron0):
            return np.sum(neuron0.voltage) * 1.345

        # Here is a another transfer function from neurons to robot messages
        # This time, the neuron parameter is explicitly mapped to an array of neurons
        # More precisely, the parameter is mapped to a group of __devices that are each connected to a single neuron
        # The neuron2 parameter will thus be a list of recorders
        @nrp.MapSpikeSink("neurons",
                          nrp.chain_neurons(nrp.brain.actors[slice(2, 4, 1)],
                                            nrp.brain.actors[slice(4, 6, 1)]),
                          nrp.leaky_integrator_alpha,
                          updates=[(1.0, [0.4, 0.4])],
                          v_rest=1.0)
        @nrp.Neuron2Robot(Husky.LeftArm.twist)
        def left_arm_tw(t, neurons):
            if neurons[0].voltage < 0.678:
                if neurons[1].voltage > 0.345:
                    return 0.756
                else:
                    return 1.123
            else:
                if neurons[1].voltage < 0.789:
                    return 0.632
                else:
                    return 0.256

        nrp.set_nest_adapter(brain)
        nrp.set_robot_adapter(robot)

        brain.__dict__["actors"] = MockPopulation(range(0, 60))
        brain.__dict__["sensors"] = MockPopulation(range(45, 645))
        config.brain_root = brain

        nrp.initialize("MyTransferFunctions")

        husky_right_arm = right_arm.topic
        husky_left_arm = left_arm_tw.topic

        brain.refresh_buffers(0.5)
        robot.refresh_buffers(0.5)
        config.active_node.run_neuron_to_robot(0.5)
        config.active_node.run_robot_to_neuron(0.5)

        brain.refresh_buffers(1.5)
        robot.refresh_buffers(1.5)
        config.active_node.run_neuron_to_robot(1.5)
        config.active_node.run_robot_to_neuron(1.5)

        self.assertIsInstance(husky_right_arm, MockPublishedTopic)
        self.assertIsInstance(husky_left_arm, MockPublishedTopic)

        self.assertEqual(len(husky_right_arm.sent), 2)
        self.assertEqual(len(husky_left_arm.sent), 2)

        self.assertEqual(husky_right_arm.sent[0], 1.345 * 2.0)
        self.assertEqual(husky_right_arm.sent[1], 1.345 * 0.3)

        self.assertEqual(husky_left_arm.sent[0], 0.256)
        self.assertEqual(husky_left_arm.sent[1], 0.756)

        config.active_node.reset()