84
Camera View
Quadrotor
Translational
DOFs
Target Translational DOFs
Ground
Two-Dimensional
Perspective
Figure 52: Arrangement for end-point closed-loop control through detection and tracking of a target.
isolates the on-board camera from the effects of rolling and pitching; or digital compensation, where
the image is slightly cropped to allow for virtual stabilisation [7]. Mechanical compensation will
retain the field of view while being faster without processing delays as compared to digital compensation, so it is recommended for mechanical compensation to be used if compensation is required [7].
However, no compensation should first be adopted to judge if it is satisfactory.
Stationary Hover
Quadrotor
Target Camera
View
Pitch / Roll
Quadrotor
Target Camera
View
Gimbal Compensation
Quadrotor
Target Camera
View
Figure 53: Comparison between the changes in the field of view during extreme/exaggerated rolling
or pitching without (middle) and with (right) compensation as the target begins a motion.
E.2 BIGQUAD APPARATUS
For the quadrotor, the Crazyflie control board was mounted to a TransTEC Freedom frame, which has
diagonals of 215mm and is constructed from 3K carbon fibre with aluminium 7075 supports for the
shell and plastic bumpers at the rotor mounts to produce a mass of 116g. The electronic components
for the flight of the quadrotor include four EMAX Bullet 30A electronic speed controllers (ESCs),
with a mass of 4.9g, BB2 processor, current rating up to 30A, and support for DShot, Multishot, and
Oneshot protocols; four EMAX RSII 2206-1700KV rotor motors, with a mass of 26.7g and maximum
thrust up to 2040N/kg; four plastic propellers, with a mass of 4g and length of 127mm; and Tattu Rline 75C LiPo battery with a mass of 160g, four cells in series, and capacity of 1300mA.hr at 14.8V.
85
To facilitate the connection between the Crazyflie and ESCs, the Bitcraze BigQuad expansion deck
(subsequently referred to as BigQuad deck) will be used. This deck features breakout header connectors for the four ESCs, where a voltage and ground will be supplied along with a PWM signal at
a default frequency of 400Hz to control the motors [30]. There are also additional breakout header


Basic usage


[ r/CrazyFlie ](/r/CrazyFlie/)


        add_config(logconf)
            """Add a log configuration to the logging framework.

            When doing this the contents of the log configuration will be validated
            and listeners for new log configurations will be notified. When
            validating the configuration the variables are checked against the TOC
            to see that they actually exist. If they don't then the configuration
            cannot be used. Since a valid TOC is required, a Crazyflie has to be
            connected when calling this method, otherwise it will fail."""


55 # Find and identify the contours within the current frame.
56 image, contours, hierarchy = cv.findContours (threshold, cv.RETR_TREE,
> cv.CHAIN_APPROX_SIMPLE) # This line should be used for OpenCV Python 3.X.
57 #contours, hierarchy = cv.findContours (processed, cv.RETR_TREE,
> cv.CHAIN_APPROX_SIMPLE) # This line should be used for OpenCV Python 4.X.
58 #temporary = cv2.findContours (des, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) #
> With the line below, this line could be used for OpenCV Python 3.X or 4.X.
59 #contours = temporary [0] if len (temporary) == 2 else temporary [1]
60
61 # Locate the largest contour and find the corresponding properties.
62 try:
63 maximum = max (contours, key = cv.contourArea)
64 moment = cv.moments (maximum)
65 centroid_x = int (moment ["m10"] / moment ["m00"])
66 centroid_y = int (moment ["m01"] / moment ["m00"])
67 area = cv.contourArea (maximum)
68 x, y, w, h = cv.boundingRect (maximum)
69 cv.rectangle (frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
70 cv.circle (frame, (centroid_x, centroid_y), 3, (255, 0, 0), cv.FILLED)
71 area = cv.contourArea (maximum)
72 transform_x = int (res_x / 2) - centroid_y
73 transform_y = int (res_y / 2) - centroid_x
74 except:
75 area = 1
72
76 transform_x = 0
77 transform_y = 0
78
79 # Draw a coordinate system and timestamp to the current frame.
80 time_current = np.around (time.time () - time_start, decimals = 1)
81 cv.putText (frame, str (time_current), (10, 30), cv.FONT_HERSHEY_SIMPLEX, 1,
> (255, 255, 255), 2) # (10, 30) = Top-Left Corner
82 cv.circle (frame, (int (res_x/2), int (res_y/2)), 5, (255, 255, 255), cv.FILLED)
83
84 # Return the relevant variables to store.
85 return frame, contours, transform_x, transform_y, area
86
87 # Function to detect the yaw orientation of the target.
88 def target_detection_orientation (frame, contours):
89


Opening and closing a communication link is done by using the Crazyflie
object:


[ Reddit, Inc. © 2024. All rights reserved. ](https://redditinc.com)


All communication links are identified using an URI built up of the following:
InterfaceType://InterfaceId/InterfaceChannel/InterfaceSpeed


Get app  Get the Reddit app  [ Log In  ](https://www.reddit.com/login/) Log in
to Reddit


    def _connected(self, link_uri):
            """ This callback is called form the Crazyflie API when a Crazyflie
            has been connected and the TOCs have been downloaded."""
            print("Connected to %s" % link_uri)

            # The definition of the logconfig can be made before connecting
            self._lg_stab = LogConfig(name="Stabilizer", period_in_ms=10)
            self._lg_stab.add_variable("stabilizer.roll", "float")
            self._lg_stab.add_variable("stabilizer.pitch", "float")
            self._lg_stab.add_variable("stabilizer.yaw", "float")

            # Adding the configuration cannot be done until a Crazyflie is
            # connected, since we need to check that the variables we
            # would like to log are in the TOC.
            try:
                self._cf.log.add_config(self._lg_stab)
                # This callback will receive the data
                self._lg_stab.data_received_cb.add_callback(self._stab_log_data)
                # This callback will be called on errors
                self._lg_stab.error_cb.add_callback(self._stab_log_error)
                # Start the logging
                self._lg_stab.start()
            except KeyError as e:


to reduce the values for each pixel and computational effort required during processing.
2. Using Equation 19, a binary threshold will be applied to the grayscale image with a threshold
value, where a pixel above this value will become white and a pixel below this value will become
black. The value will be tuned to remove the background while maintaining the target as white.
p (g) =



gnew if g > gthr
0 otherwise
(19)
Where p, pixel; g, grayscale value; gnew, new grayscale value, 255; gthr, grayscale threshold value.
B. Fundamental concept for colour processing (either A or B is performed):
1. Once the original BGR colour image has been captured, it will be converted to a HLS colour
image to provide easier isolation of the range for the distinct colour of the target.
2. Using Equation 20, a binary threshold will be applied to the HLS colour image with minimum
and maximum bounds of HLS values to create the isolation range, where a pixel within this
range will become white and a pixel outside of this range will become black. The bounds act as
a tolerance since there may be distortions in the captured colours due to environment lighting,
and the values will be tuned to remove the background while maintaining the target as white.
p (h, l, s) =



gnew if hmin < h < hmax, lmin < l < lmax, smin < s < smax
0 otherwise
(20)
Where p, pixel; h, hue value; l, lightness value; s, saturation value; gnew, new grayscale value, 255;
33
hmin, minimum hue value; hmax, maximum hue value; lmin, minimum lightness value; lmax, maximum
lightness value; and smin, minimum saturation value; smax, maximum saturation value.
C. Common target detection strategy (performed after either A or B is completed):
3. The noise in the threshold image will be reduced to a satisfactory level using a morphology
transformation with a kernel size tuned for an open transformation with an erosion transformation followed by a dilation transformation. This will ensure the target is the largest white region
in the image (unless there is a remarkably rare exception, but this will be accounted for and
mitigated by checking whether the corresponding pixel area is within the expected range).
4. The largest contour in the image will then be found based on the maximum pixel area in terms of
pixels. Assuming this contour is the target as is practically assured, the location of the centroid
of the target can be found in terms of pixels to complete the target detection (a box can also be
drawn around the contour to identify the target for monitoring confirmation).


× 100% =

log(a/b)
log(ab)/2

× 100% (30)
Where z, altitude, m; Auv, virtual image area, px2
; %pa, actual percentage difference; a, first arbitrary
variable; b, second arbitrary variable; and %pm, magnitude percentage difference.
0.10
0.20
0.30
0.40
0.50
0.60
0.70
0.80
0.90
0 200 400 600 800 1000 1200 1400 1600 1800 2000
Auv Area [px2
]
0
z Altitude [m]
Data Point
Trend Line
Trend:
Figure 37: Altitude calibration using a disk as the target with a radius of 95mm. (The uncertainty in
each altitude measurement was about ±5mm which is not distinguishable on the plot).
44
0.10


##  Logging


52 #temporary = cv2.findContours (des, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) #
> With the line below, this line could be used for OpenCV Python 3.X or 4.X.
53 #contours = temporary [0] if len (temporary) == 2 else temporary [1]
54
55 # Locate the largest contour and find the corresponding properties. Note that
> the image moments are simply weighted averages based on the pixel intensities.
56 if len (contours) != 0:
57 maximum = max (contours, key = cv.contourArea)
58 moment = cv.moments (maximum)
59 centroid_x = int (moment ["m10"] / moment ["m00"])
60 centroid_y = int (moment ["m01"] / moment ["m00"])
61 x, y, w, h = cv.boundingRect (maximum)
62 angled = cv.minAreaRect (maximum)
63 box = np.int0 (cv.boxPoints (angled))
64 cv.rectangle (frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
65 cv.drawContours (frame, [box], 0, (0, 0, 255), 2)
66 cv.circle (frame, (centroid_x, centroid_y), 3, (255, 0, 0), 5)
67 else:
68 centroid_x = 0
69 centroid_y = 0
70
71 # Draw a coordinate system and timestamp to the current frame.
72 time = str (np.around (time.time () - time_start, decimals = 1))
73 cv.putText (frame, time, (10, 30), cv.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 2)
74 cv.circle (frame, (int (res_x / 2), int (res_y / 2)), 5, (255, 255, 255), 5)
75
76 # Return the relevant variables to store.
77 return frame, contours, centroid_x, centroid_y
78
79 # Function to record and display the basic output as a video.
80 def record_minimum (frame, record):
81
82 # Record (optional) and display the basic output.
83 if record == "Y":
84 video.write (frame)
85 cv.imshow ("Original Output", frame)
86
87 # Function to record and display the complete output as a video.
88 def record_maximum (frame, mode, threshold, morphology, record, contours):
66


  * [ Book a demo ](https://snyk.io/schedule-a-demo/)
  * [ Contact us ](https://snyk.io/contact-us/)
  * [ Support ](https://support.snyk.io/hc/en-us)
  * [ Report a new vuln ](https://snyk.io/vulnerability-disclosure/)


TM4 motor four torque N.m
Tφ,+ plus-mode roll torque N.m
Tφ,× cross-mode roll torque N.m
Tψ,+ plus-mode yaw torque N.m
Tψ,× cross-mode yaw torque N.m
Tθ,+ plus-mode pitch torque N.m
Tθ,× cross-mode pitch torque N.m
τ incremental time placeholder s
τ0 temperature gradient within the lower troposphere 0.0065K/m
θ pitch rotation rad or o
x
θ¨ pitch angular acceleration rad/s2
u virtual image latitude coordinate or distance px
ui
initial latitude coordinate px
v virtual image longitude coordinate or distance px
vi
initial longitude coordinate px
W width px
x actual latitude coordinate or distance m
xq quadrotor latitude position m
xt
target latitude position m
y actual longitude coordinate or distance m
yq quadrotor longitude position m
yt
target longitude position m
z altitude m
zsea altitude relative to sea level m
z¨ acceleration along z-axis m/s2
Throughout the report, the relevant description and units are displayed with the symbols presented in
equations. If there is a discrepancy or confusion between the displayed information and this listed
information, the displayed information should be assumed to supersede this listed information.
xi
LIST OF ACRONYMS
The general acronyms used, with a corresponding description, are as follows:
3D Three-Dimensional
AMD Advanced Micro Devices
API Application Programming Interface
BGR Blue, Green, and Red


If some special properties are required for the underlying Crazyflie object, a
Crazyflie instance can be passed in to the ` SyncCrazyflie ` instance.


roll and pitch after 0.5s and stop the motors after 1s if no setpoint is received. A minimum limit
at which setpoints can be generated and sent is recommended at 10Hz.
– send_setpoint: Send a setpoint in terms of roll, pitch, and yaw angles and thrust.
– send_velocity_world_setpoint: Send a setpoint in terms of the desired absolute velocities in the x, y, and z directions with a specific yaw rate.
– send_hover_setpoint: Send a setpoint in terms of desired absolute velocities in the x
and y directions with a specific yaw rate, while a constant altitude is maintained.
– send_position_setpoint: Send a setpoint in terms of the desired absolute position with
x, y, and z coordinates and a specific yaw angle. This actually uses a velocity setpoint over
a time period since there is no direct sense of absolute position, so the error in the estimated
position may accumulate over time. The Crazyflie attempts to execute this as fast as possible
with a maximum velocity up to 1m/s as limited by default - this can easily be changed by
setting the posCtlPid.xyVelMax and posCtlPid.zVelMax parameters.
• Extpos: Send the current position of the Crazyflie measured using external sensors, which will
be directly forwarded to the position estimator of the Crazyflie.
– send_extpos: Send the current position of the Crazyflie in terms of x, y, and z coordinates.
• MotionCommander: Send control setpoints to the Crazyflie. This class is more orientated towards blocking functions, where a command is executed and the script waits until the motion is
complete before continuing, rather than just sending a setpoint and continuing immediately. So,
it is not an applicable option for real-time operations requiring variable adjustments.
If desired, further information can be found from the Bitcraze GitHub repository: https://github.
com/bitcraze/crazyflie-lib-python. A graphical client is also available which allows for
flashing firmware and interfacing with the Crazyflie using a remote controller for manual control.
4.6.2 OPENCV
OpenCV is a library of programming functions aimed at real-time computer vision. The Python implementation of OpenCV will be used through the OpenCV Python 3.2 library, which is an API with
bindings for interfacing with the actual functions written in C++ (the complete version of OpenCV is
actually written in C++, but this will be difficult to integrate with the Crazyflie Python API library and
the extra functions are not really required for appropriate target detection and tracking). The library
includes various functions for image processing as basically summarised:
• line, rectangle, circle, polylines, and putText: Draw shapes or text on the image.
• cvtColor: Convert between two colour modes. This will typically be used to convert from the
default blue, green, and red (BGR) mode to grayscale with the argument COLOR_BGR2GRAY or to
28
a hue, lightness, and saturation (HLS) mode with the argument COLOR_BGR2HLS. (It should be
emphasised that BGR is the default colour mode - not the common RGB mode).
• blur, GaussianBlur, medianBlur, or bilatitudinalFilter: Apply a blur distortion to the
image. This will typically be used to provide smoothing and remove noise.
• threshold: Apply a threshold or mask to the image. This is typically used to generate a binary
image using the argument THRESHOLD_BINARY or THRESHOLD_BINARY_INV.
• adaptiveThreshold: Apply an adaptive threshold or mask. This is typically used to consider
illumination factors from the local neighbourhood of a pixel before applying the threshold.
• inRange: Apply a threshold or mask to the image based on the colours within minimum and


  * [ GameStop  ](https://reddit.com/t/gamestop/)
  * [ Moderna  ](https://reddit.com/t/moderna/)
  * [ Pfizer  ](https://reddit.com/t/pfizer/)
  * [ Johnson & Johnson  ](https://reddit.com/t/johnson_johnson/)
  * [ AstraZeneca  ](https://reddit.com/t/astrazeneca/)
  * [ Walgreens  ](https://reddit.com/t/walgreens/)
  * [ Best Buy  ](https://reddit.com/t/best_buy/)
  * [ Novavax  ](https://reddit.com/t/novavax/)
  * [ SpaceX  ](https://reddit.com/t/spacex/)
  * [ Tesla  ](https://reddit.com/t/tesla/)


The library supports setting up logging configurations that are used for
logging variables from the firmware. Each log configuration contains a number
of variables that should be logged as well as a time period (in ms) of how
often the data should be sent back to the host. Once the log configuration is
added to the firmware the firmware will automatically send back the data at
every period. These configurations are used in the following way:


* [ Content Policy  ](https://www.redditinc.com/policies/content-policy)
* [ Privacy Policy  ](https://www.reddit.com/policies/privacy-policy)
* [ User Agreement  ](https://www.redditinc.com/policies/user-agreement)


244 crazyflie.commander.send_hover_setpoint (0, 0, (altitude / steps * i), 0)
245 time.sleep (setpoint_time)
246
247 # Ensure the Crazyflie has reached the desired altitude.
76
248 crazyflie.commander.send_hover_setpoint (0, 0, 0, altitude)
249 time.sleep (setpoint_time)
250
251 # Function to vertically land the Crazyflie.
252 def land (crazyflie, altitude):
253
254 # Print an update to the shell for monitoring.
255 print ("Crazyflie is landing from the current altitude.")
256
257 # Define the desired time in which to move and the number of setpoints to send.
258 action_time = 1
259 setpoint_time = 0.05
260 steps = int (action_time / setpoint_time)
261
262 # Loop to send the required setpoint at the desired time intervals.
263 for i in range (steps):
264 crazyflie.commander.send_hover_setpoint (0, 0, (- altitude / steps * i), 0)
265 time.sleep (setpoint_time)
266
267 # Ensure the Crazyflie has landed and becomes stationary.
268 crazyflie.commander.send_stop_setpoint ()
269 time.sleep (setpoint_time)
270
271 # --------------------------------------------------------------------------------
272 # Define Variables
273 # --------------------------------------------------------------------------------
274
275 # Set the variables for target detection and tracking.
276 print ("Setting the variables to be used.")
277 res_x = 160
278 res_y = 120
279 fps = 30
280 time_start = 0
281
282 # Set the variables for Crazyflie communication control.


###  SyncCrazyflie


* Business


#  Footer Menu


[ ![github](/advisor/images/snyk-icon-github.svg) bitcraze / crazyflie-
clients-python / lib / cfclient / ui / dialogs / logconfigdialogue.py  View on
Github  ![external](/advisor/images/snyk-icon-external.svg)
](https://github.com/bitcraze/crazyflie-clients-
python/blob/8b7e42f378aae6487281cd38ff7367954b31de7f/lib/cfclient/ui/dialogs/logconfigdialogue.py#L273)


[18] T. Antonsson, M. Eliasson and A. Taffanel. Crazyflie 2.1 Control Board Schematic. Bitcraze,
Dec. 2018. URL: https://wiki.bitcraze.io/_media/projects:crazyflie2:hardwa
re:cf2.1_component_placement.pdf.
[19] K. Richardsson et al. Flow Deck V2. Bitcraze. Online. 2019. URL: https://store.bitcra
ze.io/collections/decks/products/flow-deck-v2 (visited 12 Oct. 2019).
[20] Raspberry Pi Foundation. Raspberry Pi Zero W. Raspberry Pi. Online. 2019. URL: https:
//www.raspberrypi.org/products/raspberry-pi-zero-w/ (visited 16th Sept. 2019).
[21] J. Hughes, L. Lynch, B. Nuttall, and Raspberry Pi Foundation. Documentation - Hardware -
Camera Module. Raspberry Pi. Online. 24th April 2018. URL: https://www.raspberrypi
.org/documentation/hardware/camera/ (visited 16th Sept. 2019).
[22] R. Thornton. Raspberry Pi Zero W V1.3 - Mechanical Drawings. Raspberry Pi Foundation,
July 2019. URL: https://www.raspberrypi.org/documentation/hardware/raspber
rypi/mechanical/rpi_MECH_Zero_1p3.pdf.
[23] M. Stimson and J. Adams. Raspberry Pi Camera Module V2.1 - Mechanical Drawings. Raspberry Pi Foundation, Nov. 2015. URL: https://www.raspberrypi.org/documentation
/hardware/camera/mechanical/rpi_MECH_Camera2_2p1.pdf.
62
[24] K. Richardsson et al. Crazyradio PA USB. Bitcraze. Online. 2019. URL: https://store.bi
tcraze.io/collections/kits/products/crazyradio-pa (visited 16th Sept. 2019).
[25] A. Taffanel. Crazyflie Communication. Bitcraze. Online. Jan. 2015. URL: https://www.bit
craze.io/2015/01/crazyflie-2-0-radio-communication/ (visited 22nd Oct. 2019).
[26] DaguRobot Electronics. S4A EDU Controller (Scratch For Arduino) ROHS. Online. 2019.
URL: http://www.dagurobot.com/S4A_EDU_controller (visited 28th Sept. 2019).
[27] DaguRobot Electronics. DAGU Off Set Gear Motor Accessories 1:48 (1 Pair) 9mm Long Output Axis ROHS. Online. 2019. URL: http://www.dagurobot.com/motor_acessorios_1
:48_9MM_long_output_axis (visited 28th Sept. 2019).
[28] Qualisys. Miqus Specifications, Capture More With Less. Qualisys, Motion Capture Systems,
Oct. 2018. URL: https://cdn-content.qualisys.com/2018/10/PI_Miqus.pdf.
[29] PiShop. Raspberry Pi Zero W 1.1. Online. 2019. URL: https://www.pishop.co.za/stor
e/raspberry-pi/raspberry-pi-zero-wireless (visited 23rd Oct. 2019).
[30] T. Antonsson, M. Eliasson and A. Taffanel. BigQuad Expansion Deck. Bitcraze Wiki. Online.
11th December 2018. URL: https://wiki.bitcraze.io/projects:crazyflie2:expa
nsionboards:bigquad (visited 14th Sept. 2019).
[31] T. Antonsson, M. Eliasson and A. Taffanel. BigQuad Expansion Deck Schematic. Bitcraze,
Dec. 2018. URL: https://wiki.bitcraze.io/_media/projects:crazyflie2:expans
ionboards:bigquad-revc-top.png.
63
A ADDITIONAL RESOURCES
For supplement to the presented data, online resources containing additional data which are not of an
appropriate form for presentation in a report are available at: https://drive.google.com/open?
id=1wsVvasJ_MfNI8SphguZh88tsmAFaG_MC (access is limited to accounts linked to the University
of the Witwatersrand unless permission is requested). Since the Bitcraze Crazyflie 2.1, Bitcraze Flow


Search for:  __


√
Auv
(18)
Where z, altitude, m; f , focal length, px; x, actual latitude distance, m; u, virtual image latitude
distance, px; y, actual longitude distance, m; v, virtual image longitude distance, px; Axy, actual area,
m2
; Auv, virtual image area, px2
; and k, camera correlation coefficient, m.px.
Camera Altitude
800mm 780mm 420mm 400mm 0mm
Target
The apparent one-dimensional length/width of the target is
expected to be inversely proportional to the altitude. Thus,
the apparent two-dimensional area of the target is expected
to be inversely proportional to the square of the altitude.
Apparent One-Dimensional Size Change
Target (Half)
Field Of View (Half)
Figure 27: Method of calibrating the camera and determining the focal length.
5.2 INITIAL QUADROTOR CONSIDERATIONS
By default, the Crazyflie is set to operate in cross-mode which is acceptable given that there are
no obvious reasons to change to plus-mode. To avoid effects from flying too near the ground, the
Crazyflie should maintain an altitude above 300mm while also remaining within the range of the
motion capture facilities. The natural behaviour of the Crazyflie while attempting to hover also needs
32
to be analysed with observations into the drift experienced without any trimming being performed, so
this can then be corrected with appropriate trimming to establish balanced behaviour if necessary.
5.3 TARGET DETECTION
A direct form of position-based visual servoing will be used, where both the current altitude of the
camera and position of the target will be estimated through a reconstruction of the three-dimensional
locations. Unfortunately, this technique will slightly suffer from the camera calibration errors.
So, the necessary information for visual servoing through target detection and tracking can be found
from the Pi Camera viewing the target. The target detection can be performed through either grayscale
or colour processing, where various filters are applied to the captured image to isolate the target - as
mentioned, the most effective and efficient method will be initial evaluated and then used in the final
tests for the best real-time performance. After considering and testing with the available techniques in
OpenCV through trials, the following developed algorithm will be performed on each captured frame
with the aim of reliable target detection while maintaining minimal computational effort:
A. Fundamental concept for grayscale processing (either A or B is performed):
1. Once the BGR original colour image has been captured, it will be converted to a grayscale image


the desired location.
Wait for 1ms to check
if the termination "Q"
key was pressed.
Send the setpoint
function for the
Crazyflie to land.
Start
End
Basic post-processing
of the overall test and
recorded video.
Convert the BGR
colour frame into a
grayscale frame.
Do a binary threshold
with a threshold of
200 and new of 255.
Apply a morphology
transformation with a
kernel matrix of 5x5.
Display the processed
frame to the user for
live monitoring.
Find the contour with
maximum area to be
assumed as the target.
Is target
area over 100px2
to 1000px2
?
Yes
Find the centroid of
the target in the new
coordinate system.
Find orientation of
the target in the new
coordinate system.
Report an issues if
any errors occurred.


The API to create and get information from LogConfig:


[ ![github](/advisor/images/snyk-icon-github.svg) bitcraze / crazyflie-
clients-python / lib / cfclient / ui / tabs / FlightTab.py  View on Github
![external](/advisor/images/snyk-icon-external.svg)
](https://github.com/bitcraze/crazyflie-clients-
python/blob/0b64640096567992bb6d0820dd1cb47cfe1dbc6a/lib/cfclient/ui/tabs/FlightTab.py#L213)


* [ Reddit  reReddit: Top posts of 2023


logconf.add_variable('pm.batteryLevel', 'FP16')


The API for the log in the Crazyflie:


M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
Figure 5: Plus-mode (+) rotor arrangement to enable the translational and rotational displacements.
6
cancelled by letting the two diagonally-opposite rotors spin clockwise while the other two diagonallyopposite rotors spin counter-clockwise [2, 8]. So, when yawing is desired in a certain direction, the
angular velocities of the two rotors spinning in the opposite direction are decreased and the angular
velocities of the two rotors spinning in the same direction are increased, which will create rotation
about the z-axis such that the total thrust remains constant without rolling or pitching [2, 8].
Stationary Hover Move Up Move Down
Pitch Forward Pitch Backward Roll Left Roll Right Yaw Left


##  Structure of the library


List of Contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
List of Figures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vi
List of Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viii
List of Symbols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
List of Acronyms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii
1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Problem Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Literature Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1 Visual Servoing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Quadrotor Dynamics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Raster Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Pinhole Camera Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 Computer Vision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.6 Location Estimation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.7 Control Principles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
4 Apparatus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.1 Quadrotor Parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4.2 Visual Servoing Parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.3 Moving Target Parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.4 Motion Capture Facilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
iii
4.5 Ground Control Laptop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.6 Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.6.1 Crazyflie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.6.2 OpenCV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.6.3 Raspbain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.6.4 Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5 Methodology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.1 Initial Camera Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.2 Initial Quadrotor Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
5.3 Target Detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5.4 Target Tracking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.5 Control And Communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.6 Target Motion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.7 Data Monitoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
6 Data Analysis And Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
6.1 Camera Observations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40


* Celebrity


however, i get the incorrect values


regarding the voltage level, when the drone is stationary, the voltage level
is around 3.8 and when the drone is flying, it is at 3.4.


with SyncCrazyflie(URI, cf=Crazyflie(rw_cache='./cache')) as scf:


  * [ Blog ](/blog/ "Blog")
  * Products
    * [ Crazyflie 2.1 ](/products/crazyflie-2-1/ "Crazyflie 2.1")
    * [ Crazyflie Bolt 1.1 ](/products/crazyflie-bolt-1-1/ "Crazyflie Bolt 1.1")
    * [ Crazyradio PA ](/products/crazyradio-pa/ "Crazyradio PA")
    * [ Crazyradio 2.0 ](/products/crazyradio-2-0/ "Crazyradio 2.0")
    * [ Debug adapter kit ](/products/debug-adapter-kit/ "Debug adapter kit")
    * [ Battery charger ](/products/battery-chg-500mA/ "Battery charger")
    * [ Propellers 47-17 ](/products/propellers-47-17/ "Propellers 47-17")
    * * * *


        param_name = "group.name"
        param_value = 3
        crazyflie.param.set_value(param_name, param_value)


        # The definition of the logconfig can be made before connecting
        self._lg_stab = LogConfig(name='Stabilizer', period_in_ms=10)
        self._lg_stab.add_variable('stabilizer.roll', 'float')
        self._lg_stab.add_variable('stabilizer.pitch', 'float')
        self._lg_stab.add_variable('stabilizer.yaw', 'float')


###  Attitude Setpoints


[ ![Bitcraze logo](/images/nav/navLogo.png) ](/)


The datatype is the transferred datatype, it will be converted from internal
type to transferred type before transfers:


If this is not the case then the logging framework should be used instead.


when the drone is flying, i.e., the motors are running, the battery percentage
is always at 10. when the drone is stationary, the battery level is at 60-70.
it doesnt show any other value like 12, 38 etc. only multiples of 10.


=

T
0.0065K/m 1 −
 p
101325Pa
0.19026
(15)
Where zsea, altitude relative to sea level, m; T, temperature, K (if this cannot be measured, T0 as the
temperature at the lower troposphere limit may be used, 288.15K); τ0, temperature gradient within
the lower troposphere, 0.0065K/m; p, pressure, Pa; p0, pressure at the lower troposphere limit, 21mm Top View Bottom View
Laser Ranger
Optical Flow
28mm 28mm
Pin Holes
Forward
Figure 20: Photographs showing the top and bottom views of the Bitcraze Flow V2 expansion deck.
21
101350Pa; R, universal gas constant, 8.315kg.m2
/(s2
.mol.K); µ, air molar mass, 0.02896kg/mol; and
g, gravitational acceleration, 9.807m/s2
. This is an approximation of the hypsometric formula [2].
The rotor motors, propellers, and battery are briefly mentioned, although there is only minimal information available. The rotor motors are generic brushed DC motors, which are coreless and provide
fast accelerations and angular velocities up to approximately 21000rev/min [4, 16]. The propellers
have a length of 45mm and are made from plastic to mitigate the possibility of damages occurring
during a collision or crash. The battery is lithium-polymer (LiPo), supplies 3.7V, and has a capacity of
250mA.hr with a discharge rate of approximately 15C for a continuous flight time of approximately
7min, depending on the manoeuvres performed - there is also an internal protection circuit module to
preserve the cycle life and prevent over-charging, under-charging, and shorting [4, 16, 17].
4.2 VISUAL SERVOING PARTS
To perform visual servoing and image processing, the mentioned Raspberry Pi Zero W 1.1 (subsequently referred to as Raspberry Pi) and Raspberry Pi Camera Module 2.1 (subsequently referred
to as Pi Camera) will be used. The Raspberry Pi is a single-board computer with a mass of 8g, wireless LAN 802.11n, and Bluetooth 4.1, and features a 1.0GHz BCM2835 single-core CPU, 512MB of
RAM, 40-pin GPIO header, micro-SD card slot for flash memory (a SanDisk 16GB micro-SD card
will be used), mini-HDMI port for display, micro-USB port for power at 0.140A and 5.1V (with the
Pi Camera, the required current may increase so a supply capable of 2.1A will be used), micro-USB
port for peripherals, and CSI camera connector for communication with the Pi Camera [20].
The Pi Camera is designed to detect frequencies of light over the visible light range and has a mass
of 3g, focal length of 3.04mm, focal ratio of 2.0, latitudinal field of view of 62.2o
, and longitudinal


Max.
First
Average
Second
Average Average
Altitude
Change
Jitter
Altitude
Change
Jitter
Min.
Overshoot
Deviation
Figure 45: Relevant results against time throughout the second trial of the circular motion test.
Overshoot
Average Average
Max.
Min.
Deviation
Figure 46: Relevant results against time throughout the first trial of the combined motion test.
Max.
Min.
Average
Overshoot
Average
Deviation
Figure 47: Relevant results against time throughout the second trial of the combined motion test.
of the target and response from the Crazyflie, especially when the target moved at higher speeds - the
speed of the target ranged from stationary up to approximately 0.215m/s, which can be represented in
terms of pixels using the pinhole camera model with Equation 32. This lag, along with the resulting
position deviation between the relative position of the target and Crazyflie, are outlined in Table 3
with a comparison of the position deviation if the average lag is compensated (for discussion, the
average values are more relevant since the magnitude of the deviation is being considered).
m = f
n
z
−→ m˙ = f
n˙
z


Research Report
Quadrotor Visual Servoing
For Moving Target Tracking
MECN4006 - Research Project
Edward Rycroft
Student Number: 1478715
Supervisor: Ms. C. Kuchwa-Dube
A project report submitted to the Faculty of Engineering and the Built Environment, University of the
Witwatersrand, Johannesburg, in partial fulfilment of the requirements for the degree of Bachelor of
Science in Engineering (Mechanical).
Johannesburg, October 2019
ABSTRACT
An investigation into position-based visual servoing through end-point open-loop control was conducted for estimation of the three-dimensional position and yaw orientation of a moving target using
a single camera, where a Bitcraze Crazyflie 2.1 quadrotor then tracked this position and yaw orientation autonomously. Moreover, inexpensive and low-end hardware with marginal computational effort
was used in the form of a Raspberry Pi Zero W 1.1 and Raspberry Pi Camera Module 2.1 for image
processing with OpenCV to utilise computer vision techniques. Comparing grayscale and colour processing for initial target detection, it was evident that grayscale processing allowed for an increased
frame rate compared to colour processing by an average percentage difference of 23.2% while also
eliminating more background noise for a better interpretation of the target. During the implementation, it was found that the most satisfactory resolution was 160px by 120px for lightweight processing,
where the capability to generate setpoints for the quadrotor was at a frequency of 30.1Hz. The pinhole
camera model was also validated and successfully implemented at a resolution of 160px by 120px
which resulted in a focal length of 113.5px found through experimental calibration. This allowed for
the pinhole camera model to be used to develop a control script to reconstruct the state of the target
relative to the camera with the centroid of the target providing latitude and longitude coordinates, area
of the target providing the altitude of the camera, and rotation providing the yaw orientation when
using a disproportioned marker. With this information, the quadrotor was able to successfully mirror the translational and rotational motion of the target while maintaining the altitude of the camera.
Over eight independent tests, the real-time effectiveness was captured by a Qualisys motion capture
system and exhibited an average lag of 0.732s, average position deviations of 83.4mm, minimum yaw
orientation deviation of 9.54o
, and maximum yaw orientation deviation of 9.87o
. However, if the
average lag is compensated in each test, the average position deviation reduces to only 54.0mm, while
the minimum and maximum yaw orientation deviations reduce to only 4.76o
and 4.46o
respectively.
An altitude could also be maintained effectively, where the quadrotor only drifted by an average of
3.75% downwards or 3.98% upwards before returning to the correct altitude.
ii
LIST OF CONTENTS
Individual Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
Abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ii


than 100ms between frames, so at least 10 setpoints can be generated per second. For a realistic
result, practical cases will be considered to represent realistic use where there is no image processing,
a minimal amount of image processing with only primary information for basic monitoring, and a
high level of image processing with the additional extraction of secondary information for complete
monitoring. Both grayscale and colour processing will be compared to evaluate the most effective and
efficient method. For an accurate result, the average of three tests for each case with each resolution
will be compared. The developed control script is available in Appendix B, where only OpenCV was
employed which is valid since the tests are being compared directly against each other.
Warranting a lack of distortion and successful real-time performance, the pinhole camera model needs
to be validated for the purpose of determining the desired position for the Crazyflie. So, the camera
needs to be calibrated which will be based on a relationship for the current altitude in terms of the
focal length of the camera, actual target area, and virtual target area, as found through manipulating
the pinhole camera model relationships in Equation 16 for the disk target. Because the focal length of
the camera and actual target area will be constant, this reveals an experimental correlation coefficient
unique to the camera which can then be used in calculations with only the virtual target area.
31
Thus, a calibration will be performed where the camera is moved between an altitude of approximately 300mm to 800mm in increments of 20mm while the virtual target area is detected and recorded,
as demonstrated in Figure 27. At each altitude increment, the target will be positioned in the field
of view at the centre, top-left, top-right, bottom-left, and bottom-right regions to obtain an average
value of the virtual target area and a gauge of the likely error and uncertainty in the measurements.
This allows for a trend to be extracted from the recorded data, which can also be used to estimate the
altitude of the camera. Considering the rectangle target, the same relationship can be derived from the
pinhole camera model, as demonstrated in Equation 17, which presents the option for confirmation
with reiteration of the calibration. The proportional relationships are also simplified in Equation 18.
z = f
x
u
= f
y
v
−→ z = f
s
πx
2/4
πu
2/4
= f
s
πy
2/4


2.1 VISUAL SERVOING
Due to the aim of the research, there will be a focus on visual servoing through end-point open-loop
control, rather than end-point closed-loop control. This may employ position-based visual servoing
or image-based visual servoing. For position-based visual servoing, information is extracted from an
image based on features in the image and used to reconstruct the current three-dimensional position of
the target, where this can then be combined with the knowledge about the three-dimensional position
of the quadrotor to generate control and actuation corrections such that the quadrotor moves to the
desired position to follow the target [5]. For image-based visual servoing, an error signal is computed
directly from the features in an image, without performing a three-dimensional reconstruction, where
this error signal is then used to generate control and actuation corrections to match the current image
features with a desired arrangement [5]. It is also possible to use a mixed scheme with a combination
of aspects from both position-based visual servoing and image-based visual servoing [5].
Considering position-based visual servoing, the primary advantage is the convenience of generating
a setpoint as an absolute coordinate in three-dimensional space, but the primary disadvantage is the
uncertainty in the results of the three-dimensional reconstruction since there is a dependence on the
optical parameters, calibration of the vision system, and requisite knowledge about the dimensions of
the target prior to implementation. Considering image-based visual servoing, the primary advantage
is the insensitivity of the results to camera calibration, but the primary disadvantage is the necessity to
relate the features in the image to actions through non-linear or empirical relationships. The methods
of position-based visual servoing and image-based visual servoing are illustrated in Figure 3 for an
image, where these processes can then be repeated for each frame in a sequence.
Motion Track
Controller
Image / Video
Processing
Error
Current Image
Detect
Desired Image
Compare
Setpoint
Motion Track
Controller
3D Position
Reconstruction
Extract Compare Current Image
Error
Desired 3D
Position Setpoint
Position-Based


        # Connect to a Crazyflie
        with SyncCrazyflie(uri) as scf:
            # Create a log configuration
            log_conf = LogConfig(name='myConf', period_in_ms=200)
            log_conf.add_variable('stateEstimateZ.vx', 'int16_t')

            # Start logging
            with SyncLogger(scf, log_conf) as logger:
                # Iterate the logger to get the values
                count = 0
                for log_entry in logger:
                    print(log_entry)
                    # Do useful stuff
                    count += 1
                    if (count > 10):
                        # The logging will continue until you exit the loop
                        break
                # When leaving this "with" section, the logging is automatically stopped
            # When leaving this "with" section, the connection is automatically closed


Yaw Right
Medium Speed
Low Speed
Forward Direction
Left Direction
High Speed M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3
M4
M1
M2
M3


129 print ("! The input to record the source is invalid. Quitting the program.")
130 raise SystemExit
131
132 # ----------------------------------------------------------------------------------
67
133 # Perform Checks
134 # ----------------------------------------------------------------------------------
135
136 # Identify the source for use and recording if desired.
137 print ("Identifying the source for use and recording if desired.")
138 source = cv.VideoCapture (0)
139 source.set (cv.CAP_PROP_FRAME_WIDTH, res_x) # Alternate identifier: 3.
140 source.set (cv.CAP_PROP_FRAME_HEIGHT, res_y) # Alternate identifier: 4.
141 if record == "Y":
142 codec = cv.VideoWriter_fourcc (*"XVID")
143 if version == "B":
144 video = cv.VideoWriter ("Out.avi", codec, fps, (res_x, res_y), True)
145 elif version == "C":
146 video = cv.VideoWriter ("Out.avi", codec, fps, (res_x * 2, res_y * 2), True)
147
148 # Ensure the source is open and available.
149 active = True
150 strikes = 0
151 if source.isOpened == False:
152 print ("! The source is not open. Trying to open the source.")
153 source.Open ()
154 if source.isOpened == False:
155 print ("The source is not open.")
156 active == False
157 elif source.isOpened == True:
158 print ("The source is open and available.")
159
160 # Check that the source is operating correctly.
161 print ("Checking that the source is operating correctly.")
162 check = input ("Should the source be checked to be capturing correctly? [Y/N] ")
163 if check == "Y":
164 correct, frame = source.read ()
165 if correct == False:
166 print ("A frame could not be read correctly.")
167 correct = "N"


# -*- coding: utf-8 -*-
#
#     ||          ____  _ __
#  +------+      / __ )(_) /_______________ _____  ___
#  | 0xBC |     / __  / / __/ ___/ ___/ __ `/_  / / _ \
#  +------+    / /_/ / / /_/ /__/ /  / /_/ / / /_/  __/
#   ||  ||    /_____/_/\__/\___/_/   \__,_/ /___/\___/
#
#  Copyright (C) 2014 Bitcraze AB
#
#  Crazyflie Nano Quadcopter Client
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA  02110-1301, USA.
"""
Simple example that connects to the first Crazyflie found, logs the Stabilizer
and prints it to the console. After 10s the application disconnects and exits.
"""
import logging
import time
from threading import Timer


* [ Reddit  reReddit: Top posts of August 2023


The first thing to do is to find a Crazyflie quadcopter that we can connect
to. This is done by queuing the library that will scan all the available
interfaces (currently the debug and radio interface).


  * stabilizer.roll
  * stabilizer.pitch
  * pm.vbat
  * imu_tests.MPU6050
  * pid_attitide.pitch_kd


logconf = LogConfig(name='Position', period_in_ms=10)


###  MotionCommander


Bitcraze Cazyflie quadcopter client


extremely noisy and will be inaccurate [2, 11]. To accurately stabilise the position of the Crazyflie, it
is required for additional sensors to perform measurements relative to the external environment [16].
20
Motor Drivers
Process Microcontroller
Power Management
Protection Circuit Mod.
Micro-USB Charging
LiPo Battery
Radio Microcontroller
Power Push Button
Barometer
Gyroscope
Accelerometer
PWM
I2C SPI
Brushed Motors
Bluetooth Module
EEPROM
UART
Figure 19: Interactions of the on-board microcontrollers and sensors of the Bitcraze Crazyflie 2.1.
So, the Bitcraze Flow V2 expansion deck (subsequently referred to as Flow deck) in Figure 20 will
be mounted at the bottom on the control board to provide steady flight by detecting motion in any
direction, which can then compensate for unwanted motion or measure desired motion [19]. The
Flow deck uses a VL53L1x ToF sensor for laser-ranging and measuring the altitude to the ground up
to 4m with a field of view of 25o
and an accuracy on the order of millimetres, and a PMW3901 sensor
to employ optical flow and measure movements of the ground with a field of view of 42o
[19].
zsea =

T
τ0

1 −

p
p0
−Rτ0/(µg)
!


The group should be used to bundle together logical groups, like everything
that deals with the stabilizer should be in the group ` stabilizer ` .


[ ![github](/advisor/images/snyk-icon-github.svg) bitcraze / crazyflie-
clients-python / lib / cfclient / ui / tabs / FlightTab.py  View on Github
![external](/advisor/images/snyk-icon-external.svg)
](https://github.com/bitcraze/crazyflie-clients-
python/blob/0b64640096567992bb6d0820dd1cb47cfe1dbc6a/lib/cfclient/ui/tabs/FlightTab.py#L261)


> contours) # Uncomment for yaw orientation tracking.
397 monitor_minimum (frame, record)
398
399 # Estimate the camera altitude and target centroid position.
400 if area < 100 or area > 1000:
401 print ("The detected area is inconsistent with expectations.")
402 land (crazyflie, position_z)
403 crazyflie.commander.send_stop_setpoint ()
404 active = False
405 break
406 position_z = 9.128 * area ** -0.484 # Disk Target
407 ##position_z = 13.47 * area ** -0.492 # Rectangle Target
408 position_x = centroid_x * (position_z / focal_length)
409 position_y = centroid_y * (position_z / focal_length)
410 ##yaw = orientation # Uncomment for yaw orientation tracking.
411
412 # Based on the target, send a position setpoint to the Crazyflie.
413 crazyflie.commander.send_position_setpoint (position_x, position_y,
> position_z, yaw)
414
415 # Display the target centroid position and camera altitude. This
> dramatically slows down video capture due to printing to the shell.
416 #monitor_text (centroid_x, centroid_y, position_z, orientation)
417
80
418 # Look for the designated key to terminate the capture.
419 key = cv.waitKey (1)
420 if key == ord ("Q"):
421 print ("Termination key from [Q] was detected.")
422 land (crazyflie, position_z)
423 crazyflie.commander.send_stop_setpoint ()
424 active = False
425
426 # Look for the designated key to terminate the capture.
427 except KeyboardInterrupt:
428 print ("KeyboardInterrupt from [ctrl+c] was detected.")
429 land (crazyflie, altitude)
430 crazyflie.commander.send_stop_setpoint ()
431 active = False
432


  * [ Valheim  ](https://reddit.com/t/valheim/)
  * [ Genshin Impact  ](https://reddit.com/t/genshin_impact/)
  * [ Minecraft  ](https://reddit.com/t/minecraft/)
  * [ Pokimane  ](https://reddit.com/t/pokimane/)
  * [ Halo Infinite  ](https://reddit.com/t/halo_infinite/)
  * [ Call of Duty: Warzone  ](https://reddit.com/t/call_of_duty_warzone/)
  * [ Path of Exile  ](https://reddit.com/t/path_of_exile/)
  * [ Hollow Knight: Silksong  ](https://reddit.com/t/hollow_knight_silksong/)
  * [ Escape from Tarkov  ](https://reddit.com/t/escape_from_tarkov/)
  * [ Watch Dogs: Legion  ](https://reddit.com/t/watch_dogs_legion/)


3. 640px By 480px
Record Time [s]
Total Frames [fr]
Average FPS [fr/s]
Average SPF [s/fr]
A
18.7 72 3.85 0.260
19.3 73 3.78 0.264
15.5 59 3.81 0.263
B
27.6 107 3.88 0.258
21.8 81 3.72 0.269
24.3 93 3.83 0.261
C
38.3 41 1.07 0.934
34.9 39 1.12 0.894
30.1 33 1.10 0.912
D
26.8 81 3.02 0.331
35.4 105 2.97 0.337
23.2 67 2.89 0.346
E
14.2 14 0.99 1.013
29.4 28 0.95 1.049
22.7 21 0.93 1.080
A = None, B = Min. Gray Process, C = Max. Gray Process, D = Min. Colour Process, E = Max. Colour Process.
41
Original With Overlay Grayscale Conversion
Binary Threshold Morph. With Contour
Original With Overlay Grayscale Conversion
Binary Threshold Morph. With Contour
Original With Overlay Grayscale Conversion
Binary Threshold Morph. With Contour
Original With Overlay Colour Mask
Colour Threshold Morph. With Contour
Original With Overlay Colour Mask
Colour Threshold Morph. With Contour
Original With Overlay Colour Mask
Colour Threshold Morph. With Contour
Figure 34: Example frames and processing captured at 160px by 120px (top), 320px by 240px


#  The Crazyflie Python API explanation


  * ` Message.decode(str) ` : Decodes a binary protobuf for this message and returns it in a new instance.
  * ` Message.encode(proto) ` : Serializes a message object of this class to a binary string.
  * ` Message.decode_json(str) ` : Decodes a JSON text string for this message and returns it in a new instance.
  * ` Message.encode_json(proto) ` : Serializes a message object of this class to a JSON text string.
  * ` Message.descriptor ` : Returns the ` Google::Protobuf::Descriptor ` object for this message.


Repositories


I am really confused on how to implement the REPEATED INT32 XAXIS as If I use
the same function in function_callbacks.h it gives error that I cannot assign
an array to pb_callbacks_t type.


In this case, the ` Bar ` class is declared as a class inside of ` Foo ` , so
you can refer to it as ` Foo::Bar ` .


Forward


More generally, all fields that begin with the underbar character are reserved.


    print Foo::SomeEnum::VALUE_A  # => 0
    message.bar = Foo::SomeEnum::VALUE_A


###  geekco...@gmail.com


    bool Pb_Encode_Logs(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
    {
    	do
    	{
    		uint8_t data[max_msg_size];

    		size_of_log_message = Get_1_Log_Message(&data);

    		mqtt_log_pl_t current_log;
    		/*Setup the mqtt_log_pl_t callbacks*/

    		pb_encode_delimited(stream, LOG_FIELDS, &current_log)
                    /*How do I make mqtt_logs_pl_t know that is has to bump it's counter to indicate another log message was added */

    	}while( !is_log_buffer_empty && current_stream_size < 512 )
    }


Reply to author


Hi Everyone,


> repeated int32 xaxis = 2;


but in code you only assign a single item:


Query


#  Saved searches


No branches or pull requests


[ 
](https://groups.google.com/group/nanopb/attach/3816eefd406f9/encode_callbacks.c?part=0.2)


If I put the code above in a loop and keep encode message and checking the
buffer size will this build the buffer correctly?


You do not have permission to delete messages in this group


[ Privacy ](https://policies.google.com/privacy?hl=en_US) •  [ Terms
](https://policies.google.com/terms?hl=en_US)


##  Fields


Parameter


Describes the API of message objects that the protocol buffer compiler
generates for any given protocol definition.


Already on GitHub? [ Sign in
](/login?return_to=%2Fnanopb%2Fnanopb%2Fissues%2Fnew%2Fchoose) to your account


    message.bar = 0
    puts message.bar.inspect  # => :VALUE_A
    message.bar = :VALUE_B
    puts message.bar.inspect  # => :VALUE_B
    message.bar = 999
    puts message.bar.inspect  # => 999

    # Raises: RangeError: Unknown symbol value for enum field.
    message.bar = :UNDEFINED_VALUE

    # Switching on an enum value is convenient.
    case message.bar
    when :VALUE_A
      # ...
    when :VALUE_B
      # ...
    when :VALUE_C
      # ...
    else
      # ...
    end


By clicking “Sign up for GitHub”, you agree to our [ terms of service
](https://docs.github.com/terms) and [ privacy statement
](https://docs.github.com/privacy) . We’ll occasionally send you account
related emails.


Closed


Jun 12, 2019, 4:57:50 PM  6/12/19


[
![@PetteriAimonen](https://avatars.githubusercontent.com/u/922265?s=80&u=14ce5a89695c2e2e9f4a45948345741e7c9ea90a&v=4)
](/PetteriAimonen)


You should read the language guides for [ proto2 ](/programming-
guides/proto2/) or [ proto3 ](/programming-guides/proto3/) before reading this
document.


    * [ Enterprise ](/enterprise)
    * [ Teams ](/team)
    * [ Startups ](/enterprise/startups)
    * [ Education  ](https://education.github.com)


I need to create a submessage if "logs" which are variable in length and then
that submessage need to be held within another message which can tell me the
number of logs I have. I then need to pack all this data into buffer which
cannot exceed 512 bytes.


I'm not sure there is much need to encode log_count - the receiver can just
count the messages when decoding them anyway.


to nanopb


Sign in to report message


    m = MyMessage.new
    raise unless !m.has_foo?
    m.foo = 0
    raise unless m.has_foo?
    m.clear_foo
    raise unless !m.has_foo?


Jun 12, 2019, 5:05:15 PM  6/12/19


Description


  * [ Notifications ](/login?return_to=%2Fnanopb%2Fnanopb)
  * [ Fork  809  ](/login?return_to=%2Fnanopb%2Fnanopb)
  * [ Star  4k  ](/login?return_to=%2Fnanopb%2Fnanopb)


The Ruby class corresponding to ` Foo ` will have members called ` name ` and
` serial_number ` with accessor methods just like regular [ fields
](/reference/ruby/ruby-generated/#fields) . However, unlike regular fields, at
most one of the fields in a oneof can be set at a time, so setting one field
will clear the others.


When you create a message, you can conveniently initialize fields in the
constructor. Here is an example of constructing and using a message:


Hi Petteri,


Declaration
TibrvStatus addField(const TibrvMsgField& field)
Purpose
Add a field object to a message.


Skip to content


Sign in to reply to author


You can’t perform that action at this time.


Sorry, something went wrong.


Show original message


I have created a function in "function_callback.h" to handle and then encode
values in encode_callbacks.c for the " int32 id "


![Petteri Aimonen's profile
photo](https://lh3.googleusercontent.com/a/default-user=s40-c)


You signed in with another tab or window. [ Reload ]() to refresh your
session.  You signed out in another tab or window. [ Reload ]() to refresh
your session.  You switched accounts on another tab or window. [ Reload ]() to
refresh your session.  Dismiss alert


**[ umanayana ](/umanayana) ** commented  Aug 19, 2018  •


For


You do not have permission to delete messages in this group


Labels


#  Help creating repeated submessages  #331


* [ GitHub Sponsors  Fund open source developers  ](/sponsors)


  * ` Message#dup ` , ` Message#clone ` : Performs a shallow copy of this message and returns the new copy.
  * ` Message#== ` : Performs a deep equality comparison between two messages.
  * ` Message#hash ` : Computes a shallow hash of the message’s value.
  * ` Message#to_hash ` , ` Message#to_h ` : Converts the object to a ruby ` Hash ` object. Only the top-level message is converted.
  * ` Message#inspect ` : Returns a human-readable string representing this message.
  * ` Message#[] ` , ` Message#[]= ` : Gets or sets a field by string name. In the future this will probably also be used to get/set extensions.


  Forward


    if message.has_submessage_field?
      raise unless message.submessage_field == nil
      puts "Submessage field is unset."
    else
      raise unless message.submessage_field != nil
      message.clear_submessage_field
      raise unless message.submessage_field == nil
      puts "Cleared submessage field."
    end


Copy link


###  Footer navigation


2 participants


    * [ Topics ](/topics)
    * [ Trending ](/trending)
    * [ Collections ](/collections)


Google apps


###  geekco...@gmail.com


  * **Number types** : the value should be a ` Fixnum ` , ` Bignum ` , or ` Float ` . The value you assign must be exactly representable in the target type. So assigning ` 1.0 ` to an int32 field is ok, but assigning ` 1.2 ` is not.
  * **Boolean fields** : the value must be ` true ` or ` false ` . No other values will implicitly convert to true/false.
  * **Bytes fields** : the assigned value must be a ` String ` object. The protobuf library will duplicate the string, convert it to ASCII-8BIT encoding, and freeze it.
  * **String fields** : the assigned value must be a ` String ` object. The protobuf library will duplicate the string, convert it to UTF-8 encoding, and freeze it.


Sorry, something went wrong.


Member


Delete


to nanopb


[ Sign in
](/login?return_to=https%3A%2F%2Fgithub.com%2Fnanopb%2Fnanopb%2Fissues%2F331)


[ ](https://github.com/)


    int_repeatedfield = Google::Protobuf::RepeatedField.new(:int32, [1, 2, 3])

    raise unless !int_repeatedfield.empty?

    # Raises TypeError.
    int_repeatedfield[2] = "not an int32"

    # Raises RangeError
    int_repeatedfield[2] = 2**33

    message.int32_repeated_field = int_repeatedfield

    # This isn't allowed; the regular Ruby array doesn't enforce types like we need.
    message.int32_repeated_field = [1, 2, 3, 4]

    # This is fine, since the elements are copied into the type-safe array.
    message.int32_repeated_field += [1, 2, 3, 4]

    # The elements can be cleared without reassigning.
    int_repeatedfield.clear
    raise unless int_repeatedfield.empty?


Hi,


![geekco...@gmail.com's profile photo](//lh3.googleusercontent.com/a-/ALV-
UjXKgJMSSRU4QVmAb2hHDKzU1cUZfUWakhee6mnB_kJIvRDVUQ=s40-c)


center_x : 2.2


  Reply all


•	Code readability.
•	Type checking.
•	Accept constants and literals directly.
(Type-specific methods yield even further advantages when extracting or updating fields.)


Either email addresses are anonymous for this group or you need the view
member email addresses permission to view the original message


* [ Pricing ](/pricing)


* Solutions


zaxis : {20,50,40,60,12,14,25,19, ....}


* [ The ReadME Project  GitHub community articles  ](/readme)


Sign in to report message


The message classes also define the following methods as static. (In general
we prefer static methods, since regular methods can conflict with field names
you defined in your .proto file.)


Repeated fields are represented using a custom class `
Google::Protobuf::RepeatedField ` . This class acts like a Ruby ` Array ` and
mixes in ` Enumerable ` . Unlike a regular Ruby array, ` RepeatedField ` is
constructed with a specific type and expects all of the array members to have
the correct type. The types and ranges are checked just like message fields.


field


For proto2 messages, oneof members have individual ` has_...? ` methods as
well:


center_y : 3.2


Member


    message = Foo.new

    # Fields have their defaults.
    raise unless message.name == ""
    raise unless message.serial_number == 0
    raise unless message.test_oneof == nil

    message.name = "Bender"
    raise unless message.name == "Bender"
    raise unless message.serial_number == 0
    raise unless message.test_oneof == :name

    # Setting serial_number clears name.
    message.serial_number = 2716057
    raise unless message.name == ""
    raise unless message.test_oneof == :serial_number

    # Setting serial_number to nil clears the oneof.
    message.serial_number = nil
    raise unless message.test_oneof == nil


Search


Copy link


Sorry, something went wrong.


Sign in to reply to author


[
![@umanayana](https://avatars.githubusercontent.com/u/34135197?s=80&u=2f4a514b0e2ce5c2d97488daab852ae11d37538d&v=4)
](/umanayana)


pb_encode_delimited() doesn't work inside callbacks, instead you should use
pb_encode_tag() and then pb_encode_submessage().


    pb_encode_tag(stream, PB_WT_STRING, mqtt_log_pl_t_serial_tag);
    pb_encode_bytes(...);
    pb_encode_tag(stream, PB_WT_STRING, mqtt_log_pl_t_logs_tag);
    pb_encode_bytes(...);


###  Repeated Fields


0 new messages


•	Add Scalar.
•	Add Array.
•	Add Nested Message.
•	Add String.
•	Add Opaque Byte Sequence.
•	Add XML Byte Sequence.
•	Add DateTime.
See Also
Add Scalar


unread,


No automatic ` #to_s ` , ` #to_i ` , etc. calls will happen to perform
automatic conversion. You should convert values yourself first, if necessary.


[
![@PetteriAimonen](https://avatars.githubusercontent.com/u/922265?s=40&u=14ce5a89695c2e2e9f4a45948345741e7c9ea90a&v=4)
](/PetteriAimonen) [ PetteriAimonen ](/PetteriAimonen) closed this as [
completed
](/nanopb/nanopb/issues?q=is%3Aissue+is%3Aclosed+archived%3Afalse+reason%3Acompleted)
Nov 10, 2018


Sign up for GitHub


Include my email address so I can be contacted


    if message.submessage_field.nil?
      puts "Submessage field is unset."
    else
      message.submessage_field = nil
      puts "Cleared submessage field."
    end


Reply to author


Delete


message mqtt_logs_pl_t
{
required uint32 log_count = 1;
required mqtt_log_pl_t logs = 2;
}`


By Solution


Show original message


Copy link


  * [ Protocol Buffers  ](/)
    * [ Overview  ](/overview/)
    * [ News  ](/news/)
    * [ Programming Guides  ](/programming-guides/)
      * [ Language Guide (proto 2)  ](/programming-guides/proto2/)
      * [ Language Guide (proto 3)  ](/programming-guides/proto3/)
      * [ Style Guide  ](/programming-guides/style/)
      * [ Enum Behavior  ](/programming-guides/enum/)
      * [ Encoding  ](/programming-guides/encoding/)
      * [ Techniques  ](/programming-guides/techniques/)
      * [ Add-ons  ](/programming-guides/addons/ "Third-Party Add-ons")
      * [ Extension Declarations  ](/programming-guides/extension_declarations/)
      * [ Field Presence  ](/programming-guides/field_presence/ "Application Note: Field Presence")
      * [ Proto Serialization Is Not Canonical  ](/programming-guides/serialization-not-canonical/)
      * [ Proto Best Practices  ](/programming-guides/dos-donts/)
      * [ API Best Practices  ](/programming-guides/api/)
    * [ Protobuf Editions  ](/editions/)
      * [ Overview  ](/editions/overview/ "Protobuf Editions Overview")
      * [ Feature Settings for Editions  ](/editions/features/)
    * [ Tutorials  ](/getting-started/)
      * [ C++  ](/getting-started/cpptutorial/ "Protocol Buffer Basics: C++")
      * [ C#  ](/getting-started/csharptutorial/ "Protocol Buffer Basics: C#")
      * [ Dart  ](/getting-started/darttutorial/ "Protocol Buffer Basics: Dart")
      * [ Go  ](/getting-started/gotutorial/ "Protocol Buffer Basics: Go")
      * [ Java  ](/getting-started/javatutorial/ "Protocol Buffer Basics: Java")
      * [ Kotlin  ](/getting-started/kotlintutorial/ "Protocol Buffer Basics: Kotlin")
      * [ Python  ](/getting-started/pythontutorial/ "Protocol Buffer Basics: Python")
    * [ Reference Guides  ](/reference/)
      * [ C++  ](/reference/cpp/ "C++ Reference")
        * [ Generated Code Guide  ](/reference/cpp/cpp-generated/ "C++ Generated Code Guide")
        * [ Arena Allocation Guide  ](/reference/cpp/arenas/ "C++ Arena Allocation Guide")
        * [ ](/reference/cpp/api-docs-link/)
      * [ C#  ](/reference/csharp/ "C# Reference")
        * [ Generated Code Guide  ](/reference/csharp/csharp-generated/ "C# Generated Code Guide")
        * [ ](/reference/csharp/api-docs-link/)
        * [ ](/reference/csharp/api-docs/)
      * [ Dart  ](/reference/dart/ "Dart Reference")
        * [ Generated Code  ](/reference/dart/dart-generated/ "Dart Generated Code")
        * [ ](/reference/dart/api-docs-link/)
      * [ Go  ](/reference/go/ "Go Reference")


All I want to do is encode as many ` log ` messages as I can into a 512 byte
buffer. A single log message looks like ` mqtt_log_pl_t ` . All of these
method need to use the callback method to encode the data due to memory
constraints.


When using ` optional ` fields, field presence is checked by calling a
generated ` has_...? ` method. Setting any value—even the default value—marks
the field as present. Fields can be cleared by calling a different generated `
clear_... ` method. For example, for a message ` MyMessage ` with an int32
field ` foo ` :


Name


[ ](https://www.google.com.hk/intl/en/about/products?tab=gh)


to nan...@googlegroups.com


Sorry, something went wrong.


Sign in to reply to author


#  Provide feedback


The field name _data_ is reserved. Programs may not add fields with this name.


unread,


  * [ Code  ](/nanopb/nanopb)
  * [ Issues  45  ](/nanopb/nanopb/issues)
  * [ Pull requests  9  ](/nanopb/nanopb/pulls)
  * [ Actions  ](/nanopb/nanopb/actions)
  * [ Projects  0  ](/nanopb/nanopb/projects)
  * [ Security  ](/nanopb/nanopb/security)
  * [ Insights  ](/nanopb/nanopb/pulse)


###  Petteri Aimonen


[ nanopb ](/nanopb) /  **[ nanopb ](/nanopb/nanopb) ** Public


An enum module also defines the following utility methods:


Ruby message classes define accessors for each field, and also provide the
following standard methods:


Closed


Hi Petteri,


to nanopb


Hi Petteri,

Thanks for your response. I have implemented the repeated field and by giving
values to it using an array it encodes and I can decode it too.

But, I want to pass the values to the repeated field using an array or array
pointer by using the function that I have defined in the header file and I
call it in main several times as below:

void messer_add_number(Messer * list, int32_t id, int32_t energy, float
center_x, float center_y, int32_t xaxis[5])
{
if (list->numbers_count < MAX_NUMBERS)
{
(list->rep[list->numbers_count]).id = id;
(list->rep[list->numbers_count]).energy = energy;
(list->rep[list->numbers_count]).center_x = center_x;
(list->rep[list->numbers_count]).center_y = center_y;
(list->rep[list->numbers_count]).xaxis = xaxis[list->numbers_count];
list->numbers_count++;
}
}

messer_add_number(&actualData, 3, 60, 2.5, 5.4, {30,40,0});

but I get the following error:

build/callbacks/function_callback.h:23:52: error: incompatible types when
assigning to type ‘pb_callback_t’ {aka ‘struct pb_callback_s’} from type
‘int32_t’ {aka ‘int’}
(list->rep[list->numbers_count]).xaxis = xaxis[list->numbers_count];

How do I update my FUNCTION ?? Looking forward to your response.
Attaching the new files.


**Here is the Proto file so far**
`message mqtt_log_pl_t
{
repeated bytes serial = 1;
repeated bytes logs = 2;
}


[ umanayana ](/umanayana) opened this issue  Aug 17, 2018  · 8 comments


    int_string_map = Google::Protobuf::Map.new(:int32, :string)

    # Returns nil; items is not in the map.
    print int_string_map[5]

    # Raises TypeError, value should be a string
    int_string_map[11] = 200

    # Ok.
    int_string_map[123] = "abc"

    message.int32_string_map_field = int_string_map


yaxis : {20,50,40,60,12,14,25,19, ....}


Either email addresses are anonymous for this group or you need the view
member email addresses permission to view the original message


![Grooo's profile photo](//lh3.googleusercontent.com/a-/ALV-
UjUM6ZnEgKmEvGA-0t1yXcgpUa-JGm31qAjI28aHoFaxvra2UGDS_A=s40-c)


[ Protocol Buffers Documentation  ](/)


###  Singular Message Fields


It is possible to create message cycles when you assign submessages. For
example:


[ 
](https://groups.google.com/group/nanopb/attach/25028042845d5/callbacks.proto?part=0.1)


Delete


Whenever you set a field, the value is type-checked against the declared type
of that field. If the value is of the wrong type (or out of range), an
exception will be raised.


[ Sign in
](https://accounts.google.com/ServiceLogin?hl=en&passive=true&continue=https://groups.google.com&ec=GAZA0AM)


unread,


Author


  * [ __ ](https://github.com/protocolbuffers/protobuf)
  * [ __ ](https://groups.google.com/g/protobuf)


Given a message with a oneof:


Clear


edited


Password


Explore


No milestone


---

The text was updated successfully, but these errors were encountered:


Training


Forward


#  Help creating repeated submessages  #331


**Have a question about this project?** Sign up for a free GitHub account to
open an issue and contact its maintainers and the community.


Sign in to forward


So I am thinking the first callback for ` mqtt_logs_pl_t() ` lets call it `
Pb_Encode_Logs ` , continuously reads the stream in a loop. On each iteration
calls ` pb_encode_delimited() ` once the 512 buffer still has space. But what
I don't understand is how does ` mqtt_logs_pl_t ` messages know how many
repeated values it has, once it done encoding a variable number of `
mqtt_log_pl_t ` . I could have encoded 1 or 10 or 100, but I don't know how
many before hand.


##  nanopb


Additional navigation options


A message can be declared inside another message. For example:


I don't currently have enough hobby time available to make a more complete
example.


**[ PetteriAimonen ](/PetteriAimonen) ** commented  Aug 18, 2018


##  Enumerations


###  Petteri Aimonen


© 2024 Google LLC All Rights Reserved  [ Privacy Policy
](https://policies.google.com/privacy) Hosted by GitHub Pages.  [ GitHub
Privacy Statement ](https://docs.github.com/en/site-policy/privacy-
policies/github-privacy-statement)


to nan...@googlegroups.com


##  Packages


Adding Fields to a Nested Message
Rendezvous programs must use this three-step process:


Copy link


  * ` Enum#lookup(number) ` : Looks up the given number and returns its name, or ` nil ` if none was found. If more than one name has this number, returns the first that was defined.
  * ` Enum#resolve(symbol) ` : Returns the number for this enum name, or ` nil ` if none was found.
  * ` Enum#descriptor ` : Returns the descriptor for this enum.


Sign in to reply to author


* Product


The protocol compiler generates an output message with the name `
FooBar::Baz::MyMessage ` .


[
![@umanayana](https://avatars.githubusercontent.com/u/34135197?s=80&u=2f4a514b0e2ce5c2d97488daab852ae11d37538d&v=4)
](/umanayana)


Jun 6, 2019, 11:22:37 PM  6/6/19


Delete


Thanks for the help. I've managed to get it to work as I needed.


Forward


Sign in to report message


Reply to author


Member


![geekco...@gmail.com's profile photo](//lh3.googleusercontent.com/a-/ALV-
UjXKgJMSSRU4QVmAb2hHDKzU1cUZfUWakhee6mnB_kJIvRDVUQ=s40-c)


    package foo_bar.baz;

    message MyMessage {}


**[ umanayana ](/umanayana) ** commented  Aug 20, 2018  via email


You do not have permission to delete messages in this group


Copy link


![Petteri Aimonen's profile
photo](https://lh3.googleusercontent.com/a/default-user=s40-c)


Sign in to forward


When you assign a submessage, it must be a generated message object of the
correct type.


  * [ Terms ](https://docs.github.com/site-policy/github-terms/github-terms-of-service)
  * [ Privacy ](https://docs.github.com/site-policy/privacy-policies/github-privacy-statement)
  * [ Security ](/security)
  * [ Status ](https://www.githubstatus.com/)
  * [ Docs ](https://docs.github.com/)
  * [ Contact ](https://support.github.com?tags=dotcom-footer)
  * Manage cookies
  * Do not share my personal information


##  Messages


Remarks
This method copies the data into the new message field. All related convenience methods behave similarly.


###  Singular Fields


Resources


**[ PetteriAimonen ](/PetteriAimonen) ** commented  Aug 20, 2018


[ @PetteriAimonen ](https://github.com/PetteriAimonen)
After a bit of Googling and from what you said I am thinking this.


###  Petteri Aimonen


Sign in to report message


Jun 12, 2019, 3:58:21 AM  6/12/19


On each callback to ` mqtt_logs_pl_t ` just encode 1 ` mqtt_log_pl_t ` . But
obtain that log message before hand and pass it to the callback. Then just
call pb_encode() in a loop until the buffer becomes full or there no more
logs. Then just send the encode buffer.
See below? Will this build the buffer correctly so the receiver can iterate
over the multiples logs


> (list->rep[list->numbers_count]).xaxis = xaxis[list->numbers_count];


Reply to author


No one assigned


Show original message


to nanopb


You should _not_ create your own ` Foo ` subclasses. Generated classes are not
designed for subclassing and may lead to "fragile base class" problems.


[ ](https://github.com "GitHub") © 2024 GitHub, Inc.


[
![@PetteriAimonen](https://avatars.githubusercontent.com/u/922265?s=80&u=14ce5a89695c2e2e9f4a45948345741e7c9ea90a&v=4)
](/PetteriAimonen)


callbacks.proto


Sign in to forward


[ 
](https://groups.google.com/group/nanopb/attach/3816eefd406f9/function_callback.h?part=0.3)


You can refer to enum values like so:


Toggle navigation


All groups and messages


Field Name Length
The constant TIBRVMSG_FIELDNAME_MAX (127) determines the longest possible field name.


Main menu


energy : 50


  * This message holds a single log message.
  * ` log ` : is a byte array of varying size. I am think of putting this into it's own messages and serialise it using ` pb_encode_delimited() ` so I can encode the size with it.
  * ` serial ` : Fixed length of 4 bytes. But I don't specify it as fixed because I want to use the callback to get the serial number and encode directly into buffer.


None yet


You could make use of the protobuf feature that appending messages in binary
format appends the data in them. So you can set (nanopb).max_count = 1 on the
mqtt_logs_pl_t field logs, and then just do:


Is there better example of how this can be done? As I'm not following your
suggestions.


[ Sign up
](/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-
name%3E%2F%3Crepo-
name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-
repo&source_repo=nanopb%2Fnanopb)


Pick a username


Conversations


Projects


Report message


Search or jump to...


\--
Petteri


The package name defined in the ` .proto ` file is used to generate a module
structure for the generated messages. Given a file like:


Reply to author


> I am really confused on how to implement the REPEATED INT32 XAXIS as
> If I use the same function in function_callbacks.h it gives error that
> I cannot assign an array to pb_callbacks_t type.


[ __ View page source
](https://github.com/protocolbuffers/protocolbuffers.github.io/tree/main/content/reference/ruby/ruby-
generated.md) [ __ Edit this page
](https://github.com/protocolbuffers/protocolbuffers.github.io/edit/main/content/reference/ruby/ruby-
generated.md) [ __ Create child page
](https://github.com/protocolbuffers/protocolbuffers.github.io/new/main/content/reference/ruby/ruby-
generated.md?filename=change-
me.md&value=---%0Atitle%3A+%22Long+Page+Title%22%0AlinkTitle%3A+%22Short+Nav+Title%22%0Aweight%3A+100%0Adescription%3A+%3E-%0A+++++Page+description+for+heading+and+indexes.%0A---%0A%0A%23%23+Heading%0A%0AEdit+this+template+to+create+your+new+page.%0A%0A%2A+Give+it+a+good+name%2C+ending+in+%60.md%60+-+e.g.+%60getting-
started.md%60%0A%2A+Edit+the+%22front+matter%22+section+at+the+top+of+the+page+%28weight+controls+how+its+ordered+amongst+other+pages+in+the+same+directory%3B+lowest+number+first%29.%0A%2A+Add+a+good+commit+message+at+the+bottom+of+the+page+%28%3C80+characters%3B+use+the+extended+description+field+for+more+detail%29.%0A%2A+Create+a+new+branch+so+you+can+preview+your+new+file+and+request+a+review+via+Pull+Request.%0A)
[ __ Create documentation issue
](https://github.com/protocolbuffers/protocolbuffers.github.io/issues/new?title=Ruby%20Generated%20Code%20Guide)
[ __ Create project issue
](https://github.com/protocolbuffers/protobuf/issues/new)


  * The extension ( ` .proto ` ) is replaced with ` _pb.rb ` .
  * The proto path (specified with the ` --proto_path= ` or ` -I ` command-line flag) is replaced with the output path (specified with the ` --ruby_out= ` flag).


* [ __ ](https://stackoverflow.com/questions/tagged/protocol-buffers)


[ Conversations  ](./g/nanopb)


Cancel  Submit feedback


Thanks

---

All reactions


Yeah, that's what I tried to show in my first reply.

But currently I don't have enough time to provide a more elaborate
example.

\--
Petteri


    message = Foo.new

    raise unless !message.has_test_oneof?
    raise unless !message.has_name?
    raise unless !message.has_serial_number?
    raise unless !message.has_test_oneof?

    message.name = "Bender"
    raise unless message.has_test_oneof?
    raise unless message.has_name?
    raise unless !message.has_serial_number?
    raise unless !message.has_test_oneof?


I took a look at submessage encoding that won't work. When I read the data if
I don't encode it and store it in the 512 byte buffer I won't have anywhere
else to store it. So I have to read the data and encode on the fly.


I have the following .proto file:


The ` RepeatedField ` type supports all of the same methods as a regular Ruby
` Array ` . You can convert it to a regular Ruby Array with `
repeated_field.to_a ` .


Either email addresses are anonymous for this group or you need the view
member email addresses permission to view the original message


New issue


    // foo.proto
    message RecursiveMessage {
      RecursiveMessage submessage = 1;
    }

    # test.rb

    require 'foo'

    message = RecursiveSubmessage.new
    message.submessage = message


    void add_log_message(void *data, pb_stream_t *stream)
    {
        mqtt_logs_pl_t logs = mqtt_logs_pl_t_init_zero;
        logs.logs_count = 1;
        logs.logs[0] = .. fill in data ..;

        size_t size = 0;
        pb_get_encoded_size(&size, mqtt_logs_pl_t_fields, &logs);

        if (stream.bytes_written + size > 512)
        {
            ... send the old buffer data, reinit stream ..
        }

        pb_encode(stream, mqtt_logs_pl_t_fields, &logs);
    }

---

All reactions


Report message


The protocol compiler for Ruby emits Ruby source files that use a DSL to
define the message schema. However the DSL is still subject to change. In this
guide we only describe the API of the generated messages, and not the DSL.


Delete


You do not have permission to delete messages in this group


Open topic with navigation


You may assign either a number or a symbol to an enum field. When reading the
value back, it will be a symbol if the enum value is known, or a number if it
is unknown. Since **proto3** uses open enum semantics, any number may be
assigned to an enum field, even if it was not defined in the enum.


The protocol buffer compiler produces Ruby output when invoked with the `
--ruby_out= ` command-line flag. The parameter to the ` --ruby_out= ` option
is the directory where you want the compiler to write your Ruby output. The
compiler creates a ` .rb ` file for each ` .proto ` file input. The names of
the output files are computed by taking the name of the ` .proto ` file and
making two changes:


[
![@umanayana](https://avatars.githubusercontent.com/u/34135197?s=80&u=2f4a514b0e2ce5c2d97488daab852ae11d37538d&v=4)
](/umanayana)


`

---

All reactions


Nah, that won't make a difference. I'm constantly overwhelmed by the
amount of free support requests on nanopb, so I just have to put a limit
on how much time I'll spend on one case.

\--
Petteri


Copy link


[ Search syntax tips ](https://docs.github.com/search-github/github-code-
search/understanding-github-code-search-syntax)


If you try to serialize this, the library will detect the cycle and fail to
serialize.


It is illegal to add a field that has both a NULL field name, and a non-zero field identifier.


              * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/-extension-list/to-string/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/-only-for-use-by-generated-proto-code/)
              * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/-only-for-use-by-generated-proto-code/-only-for-use-by-generated-proto-code/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/-proto-dsl-marker/)
              * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/-proto-dsl-marker/-proto-dsl-marker/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/contains/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/get/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/is-a/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/plus/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/set/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/to-byte-string-utf8/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/to-byte-string/)
            * [ ](/reference/kotlin/api-docs/protobuf-kotlin/com.google.protobuf.kotlin/unpack/)
        * [ Generated Code Guide  ](/reference/kotlin/kotlin-generated/ "Kotlin Generated Code Guide")
      * [ Objective-C  ](/reference/objective-c/ "Objective-C Reference")
        * [ Generated Code Guide  ](/reference/objective-c/objective-c-generated/ "Objective-C Generated Code Guide")
      * [ PHP  ](/reference/php/ "PHP Reference")
        * [ Generated Code Guide  ](/reference/php/php-generated/ "PHP Generated Code Guide")
        * [ ](/reference/php/api-docs-link/)
        * [ ](/reference/php/api-docs/)
      * [ Python  ](/reference/python/ "Python Reference")
        * [ Generated Code Guide  ](/reference/python/python-generated/ "Python Generated Code Guide")
        * [ ](/reference/python/api-docs-link/)
      * [ Ruby  ](/reference/ruby/ "Ruby Reference")
        * [ Generated Code Guide  ](/reference/ruby/ruby-generated/ "Ruby Generated Code Guide")
      * [ Protocol Buffers  ](/reference/protobuf/ "Protocol Buffers Reference")
        * [ 2023 Language Specification  ](/reference/protobuf/edition-2023-spec/ "Protocol Buffers Edition 2023 Language Specification")
        * [ Version 2 Language Specification  ](/reference/protobuf/proto2-spec/ "Protocol Buffers Version 2 Language Specification")
        * [ Version 3 Language Specification  ](/reference/protobuf/proto3-spec/ "Protocol Buffers Version 3 Language Specification")
        * [ Text Format Language Specification  ](/reference/protobuf/textformat-spec/)
        * [ Well-Known Types  ](/reference/protobuf/google.protobuf/ "Protocol Buffers Well-Known Types")
      * [ Other Languages  ](/reference/other/)
    * [ Support  ](/support/)
      * [ Version Support  ](/support/version-support/)
      * [ Migration Guide  ](/support/migration/)
      * [ Cross-Version Runtime Guarantee  ](/support/cross-version-runtime-guarantee/)
    * [ Downloads  ](/downloads/)
    * [ History  ](/history/)
    * [ Forum  ](https://groups.google.com/g/protobuf)
    * [ ](/navbar/)


##  Comments


Jun 12, 2019, 4:57:25 PM  6/12/19


So, for example, let’s say you invoke the compiler as follows:


[ 
](https://groups.google.com/group/nanopb/attach/3816eefd406f9/error_callback.png?part=0.4)


[ umanayana ](/umanayana) opened this issue  Aug 17, 2018  · 8 comments


Since all the field are repeated I plan to use ` pb_callback_t() ` for all
messages. I also can't have the C structure declare inline memory as I don't
have the memory to do that.
So all the stream need to directly write into the 512 buffer, from there
source streams.
Thus I can't prefill the structure as you suggested or did I miss understand
your suggestion?


unread,


Skip to first unread message


[ Groups  ](./my-groups "Groups")


Procedure


##  Oneof


Delete


Copy link


Search


Clear search


###  Grooo


    * [ CI/CD & Automation ](/solutions/ci-cd/)
    * [ DevOps ](/solutions/devops/)
    * [ DevSecOps  ](https://resources.github.com/devops/fundamentals/devsecops/)


Jun 7, 2019, 2:57:33 AM  6/7/19


> So, how will the nested callbacks work, could you point me towards or
> give me an example ?


    * [ All features ](/features)
    * [ Documentation  ](https://docs.github.com)
    * [ GitHub Skills  ](https://skills.github.com/)
    * [ Blog  ](https://github.blog)


[ @PetteriAimonen ](https://github.com/PetteriAimonen)
Why do I need to set the ` (nanopb).max_count ` to ` 1 ` ?


Sign in to forward


Add this field to the message.


Convenience Methods
When the datatype of a field is determined during execution, use this general method. When you can determine the datatype of a field before compile-time, we recommend using type-specific convenience methods instead of this general method. Type-specific methods yield these advantages when adding fields:


Sorry, something went wrong.


**[ umanayana ](/umanayana) ** commented  Aug 18, 2018  •


[ Sign in
](/login?return_to=https%3A%2F%2Fgithub.com%2Fnanopb%2Fnanopb%2Fissues%2F331)


Show original message


You do not have permission to delete messages in this group


Sign in to report message


The compiler will read the files ` src/foo.proto ` and ` src/bar/baz.proto `
and produce two output files: ` build/gen/foo_pb.rb ` and `
build/gen/bar/baz_pb.rb ` . The compiler will automatically create the
directory ` build/gen/bar ` if necessary, but it will _not_ create ` build `
or ` build/gen ` ; they must already exist.


    bool CallBack_mqtt_logs_pl_t(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
    {
    	/*Setup mqtt_log_pl_t callbacks
    	Encode 1 mqtt_log_pl_t and return*/
    	pb_encode_delimited(stream, mqtt_log_pl_t_field, &current_log)
    }

    Encode_As_Many_Logs_As_Possible
    {
    	/*Initialize the output stream here*/

    	do
    	{
    		uint8_t data[max_msg_size];

    		size_of_log_message = Get_1_Log_Message(&data);

    		mqtt_logs_pl_t logs
    		/*Setup the callback and data for logs*/

    		pb_encode(stream, mqtt_logs_pl_t_fields, &logs);

    	}while( !is_log_buffer_empty && current_stream_size < 512 )

    	//Send the encoded buffer here
    }

---

All reactions


    * [ Actions  Automate any workflow  ](/features/actions)
    * [ Packages  Host and manage packages  ](/features/packages)
    * [ Security  Find and fix vulnerabilities  ](/features/security)
    * [ Codespaces  Instant dev environments  ](/features/codespaces)
    * [ Copilot  Write better code with AI  ](/features/copilot)
    * [ Code review  Manage code changes  ](/features/code-review)
    * [ Issues  Plan and track work  ](/features/issues)
    * [ Discussions  Collaborate outside of code  ](/features/discussions)


Copy link


So inside the xaxis repeated field, do you want one item, a few items or
unlimited items? If it's only a few, just set a maximum count for that
field. Otherwise you'll need nested callbacks.

\--
Petteri


Author


###  Map Fields


I cannot define the size of it in .proto file as I dont know the size so I
have to use callbacks.


* [ Search Results  ](/search/)


Hello,


Map fields are represented using a special class that acts like a Ruby ` Hash
` ( ` Google::Protobuf::Map ` ). Unlike a regular Ruby hash, ` Map ` is
constructed with a specific type for the key and value and expects all of the
map’s keys and values to have the correct type. The types and ranges are
checked just like message fields and ` RepeatedField ` elements.


        * [ Generated Code Guide  ](/reference/go/go-generated/ "Go Generated Code Guide")
        * [ FAQ  ](/reference/go/faq/ "Go FAQ")
        * [ ](/reference/go/api-docs-link/)
      * [ Java  ](/reference/java/ "Java Reference")
        * [ Generated Code Guide  ](/reference/java/java-generated/ "Java Generated Code Guide")
        * [ Generated Proto Names  ](/reference/java/java-proto-names/ "Java Proto Names")
        * [ ](/reference/java/api-docs-link/)
        * [ ](/reference/java/api-docs/allclasses-frame/)
        * [ ](/reference/java/api-docs/allclasses-noframe/)
        * [ ](/reference/java/api-docs/com/google/protobuf/abstractmessage.builder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/abstractmessage/)
        * [ ](/reference/java/api-docs/com/google/protobuf/abstractmessagelite.builder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/abstractmessagelite/)
        * [ ](/reference/java/api-docs/com/google/protobuf/abstractparser/)
        * [ ](/reference/java/api-docs/com/google/protobuf/any.builder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/any/)
        * [ ](/reference/java/api-docs/com/google/protobuf/anyorbuilder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/anyproto/)
        * [ ](/reference/java/api-docs/com/google/protobuf/api.builder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/api/)
        * [ ](/reference/java/api-docs/com/google/protobuf/apiorbuilder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/apiproto/)
        * [ ](/reference/java/api-docs/com/google/protobuf/blockingrpcchannel/)
        * [ ](/reference/java/api-docs/com/google/protobuf/blockingservice/)
        * [ ](/reference/java/api-docs/com/google/protobuf/boolvalue.builder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/boolvalue/)
        * [ ](/reference/java/api-docs/com/google/protobuf/boolvalueorbuilder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/byteoutput/)
        * [ ](/reference/java/api-docs/com/google/protobuf/bytestring.byteiterator/)
        * [ ](/reference/java/api-docs/com/google/protobuf/bytestring/)
        * [ ](/reference/java/api-docs/com/google/protobuf/bytestring.output/)
        * [ ](/reference/java/api-docs/com/google/protobuf/bytesvalue.builder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/bytesvalue/)
        * [ ](/reference/java/api-docs/com/google/protobuf/bytesvalueorbuilder/)
        * [ ](/reference/java/api-docs/com/google/protobuf/codedinputstream/)
        * [ ](/reference/java/api-docs/com/google/protobuf/codedoutputstream/)
        * [ ](/reference/java/api-docs/com/google/protobuf/codedoutputstream.outofspaceexception/)
        * [ ](/reference/java/api-docs/com/google/protobuf/compiler/package-frame/)
        * [ ](/reference/java/api-docs/com/google/protobuf/compiler/package-summary/)
        * [ ](/reference/java/api-docs/com/google/protobuf/compiler/package-tree/)


unread,


Forward


Assignees


Hi,


Jun 12, 2019, 5:12:54 PM  6/12/19


Either email addresses are anonymous for this group or you need the view
member email addresses permission to view the original message


Report message


  Reply to author


But you can work it out by e.g. encoding the data you want with some test
code, and then compare the output of your more memory-efficient version until
you get it to match.

---

All reactions


To see all available qualifiers, see our [ documentation
](https://docs.github.com/search-github/github-code-search/understanding-
github-code-search-syntax) .


and all of this will be repeated a number of times.

On Wednesday, 12 June 2019 10:31:50 UTC+2, Grooo wrote:


Copy link


[ 
](https://groups.google.com/group/nanopb/attach/3816eefd406f9/callbacks.proto?part=0.1)


CPP Reference > Data > TibrvMsg > TibrvMsg::addField()
TibrvMsg::addField()
Method


id: 20


    message Foo {
      oneof test_oneof {
         string name = 1;
         int32 serial_number = 2;
      }
    }


Though if you need particularly tight control of memory usage and the encoding
process, you can bypass pb_encode() completely. Instead doing something like:


[
![@PetteriAimonen](https://avatars.githubusercontent.com/u/922265?s=80&u=14ce5a89695c2e2e9f4a45948345741e7c9ea90a&v=4)
](/PetteriAimonen)


Show original message


    message Foo {
      message Bar { }
    }


    message.foo = get_value()
    print message.foo


In addition to comparing and assigning ` nil ` , generated messages have `
has_... ` and ` clear_... ` methods, which behave the same as for basic types:


We read every piece of feedback, and take your input very seriously.


#  Ruby Generated Code Guide


unread,


1.	Extract the nested submessage (see Get Nested Message).
2.	Add the new fields to the extracted submessage, using type-specific convenience methods or this method. The field is added to a snapshot copy of the submessage, and modifies the copy rather than the original parent message.
3.	Store the modified submessage back into the field of the parent message (see Update Nested Message).
Avoiding Common Mistakes
Whenever possible, we recommend storing arrays in message fields using one of the Rendezvous array types. This strategy makes the most efficient use of storage space, processor time, and network bandwidth.


![Grooo's profile photo](//lh3.googleusercontent.com/a-/ALV-
UjUM6ZnEgKmEvGA-0t1yXcgpUa-JGm31qAjI28aHoFaxvra2UGDS_A=s40-c)


##  Compiler Invocation


Callback for ` mqtt_logs_pl_t `


to nan...@googlegroups.com


Development


> Hi Petteri,
>
>
>
>
> I was trying to assign a single item just to check if it works.
>
>
>
>
> What I want is 3 repeated fields i.e.
>
>
>
>
> repeated int32 xaxis;
>
> repeated int32 yaxis;
>
> repeated int32 zaxis;
>
>
>
>
> and they all would have unlimited values or number of values that I cannot
> fix with a maximum count. So, how will the nested callbacks work, could you
> point me towards or give me an example ?
>
>
>
>
> Thank you !


##  Use saved searches to filter your results more quickly


  * Holds an array of ` mqtt_log_pl_t ` message depends on how many I can fit into my 512 byte buffer.
  * ` log_count ` : Intended to indicate the total number of ` log ` which were encoded? I am not sure about this but I need some way to know how many log messages were encoded. Is there a better way to do this.


The data packet will look something like:


My objective here is to fill up the 512byte buffer with as much data as
possible, but I don't know how much data I can fill it with unless I start
reading the source stream. After encoding 1 ` mqtt_log_pl_t ` message I need
to bump a counter so that ` mqtt_logs_pl_t ` know it has 1 more message in it,
but how do I encode that kind of data?


Forward


![Petteri Aimonen's profile
photo](https://lh3.googleusercontent.com/a/default-user=s40-c)


For each field in a message type, there are accessor methods to set and get
the field. So given a field ` foo ` you can write:


None yet


####  Checking Presence


Author


message Cluster {
required int32 id = 1;
repeated int32 xaxis = 2;
}
message ClusterArray {
repeated Cluster clusters = 1;
}


unread,


Cancel  Create saved search


Show original message


Report message


protoc --proto_path=src --ruby_out=build/gen src/foo.proto src/bar/baz.proto


Message has been deleted


**[ PetteriAimonen ](/PetteriAimonen) ** commented  Aug 18, 2018


Copy link


YesNo
Copyright © 1997-2024. Cloud Software Group, Inc. All Rights Reserved.


Sign in to forward


That's because those are repeated fields, so to make them into (two-
dimensional) arrays, you need to specify ` max_count ` also.

---

All reactions


Sign in to reply to author


###  Nested Types